First
[anni] / test / pleroma / web / admin_api / controllers / config_controller_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.AdminAPI.ConfigControllerTest do
6   use Pleroma.Web.ConnCase
7
8   import ExUnit.CaptureLog
9   import Pleroma.Factory
10
11   alias Pleroma.ConfigDB
12
13   setup do
14     admin = insert(:user, is_admin: true)
15     token = insert(:oauth_admin_token, user: admin)
16
17     conn =
18       build_conn()
19       |> assign(:user, admin)
20       |> assign(:token, token)
21
22     {:ok, %{admin: admin, token: token, conn: conn}}
23   end
24
25   describe "GET /api/pleroma/admin/config" do
26     setup do: clear_config(:configurable_from_database, true)
27
28     test "when configuration from database is off", %{conn: conn} do
29       clear_config(:configurable_from_database, false)
30       conn = get(conn, "/api/pleroma/admin/config")
31
32       assert json_response_and_validate_schema(conn, 400) ==
33                %{
34                  "error" => "You must enable configurable_from_database in your config file."
35                }
36     end
37
38     test "with settings only in db", %{conn: conn} do
39       config1 = insert(:config)
40       config2 = insert(:config)
41
42       conn = get(conn, "/api/pleroma/admin/config?only_db=true")
43
44       %{
45         "configs" => [
46           %{
47             "group" => ":pleroma",
48             "key" => key1,
49             "value" => _
50           },
51           %{
52             "group" => ":pleroma",
53             "key" => key2,
54             "value" => _
55           }
56         ]
57       } = json_response_and_validate_schema(conn, 200)
58
59       assert key1 == inspect(config1.key)
60       assert key2 == inspect(config2.key)
61     end
62
63     test "db is added to settings that are in db", %{conn: conn} do
64       _config = insert(:config, key: ":instance", value: [name: "Some name"])
65
66       %{"configs" => configs} =
67         conn
68         |> get("/api/pleroma/admin/config")
69         |> json_response_and_validate_schema(200)
70
71       [instance_config] =
72         Enum.filter(configs, fn %{"group" => group, "key" => key} ->
73           group == ":pleroma" and key == ":instance"
74         end)
75
76       assert instance_config["db"] == [":name"]
77     end
78
79     test "merged default setting with db settings", %{conn: conn} do
80       config1 = insert(:config)
81       config2 = insert(:config)
82
83       config3 =
84         insert(:config,
85           value: [k1: :v1, k2: :v2]
86         )
87
88       %{"configs" => configs} =
89         conn
90         |> get("/api/pleroma/admin/config")
91         |> json_response_and_validate_schema(200)
92
93       assert length(configs) > 3
94
95       saved_configs = [config1, config2, config3]
96       keys = Enum.map(saved_configs, &inspect(&1.key))
97
98       received_configs =
99         Enum.filter(configs, fn %{"group" => group, "key" => key} ->
100           group == ":pleroma" and key in keys
101         end)
102
103       assert length(received_configs) == 3
104
105       db_keys =
106         config3.value
107         |> Keyword.keys()
108         |> ConfigDB.to_json_types()
109
110       keys = Enum.map(saved_configs -- [config3], &inspect(&1.key))
111
112       values = Enum.map(saved_configs, &ConfigDB.to_json_types(&1.value))
113
114       mapset_keys = MapSet.new(keys ++ db_keys)
115
116       Enum.each(received_configs, fn %{"value" => value, "db" => db} ->
117         db = MapSet.new(db)
118         assert MapSet.subset?(db, mapset_keys)
119
120         assert value in values
121       end)
122     end
123
124     test "subkeys with full update right merge", %{conn: conn} do
125       insert(:config,
126         key: ":emoji",
127         value: [groups: [a: 1, b: 2], key: [a: 1]]
128       )
129
130       insert(:config,
131         key: ":assets",
132         value: [mascots: [a: 1, b: 2], key: [a: 1]]
133       )
134
135       %{"configs" => configs} =
136         conn
137         |> get("/api/pleroma/admin/config")
138         |> json_response_and_validate_schema(200)
139
140       vals =
141         Enum.filter(configs, fn %{"group" => group, "key" => key} ->
142           group == ":pleroma" and key in [":emoji", ":assets"]
143         end)
144
145       emoji = Enum.find(vals, fn %{"key" => key} -> key == ":emoji" end)
146       assets = Enum.find(vals, fn %{"key" => key} -> key == ":assets" end)
147
148       emoji_val = ConfigDB.to_elixir_types(emoji["value"])
149       assets_val = ConfigDB.to_elixir_types(assets["value"])
150
151       assert emoji_val[:groups] == [a: 1, b: 2]
152       assert assets_val[:mascots] == [a: 1, b: 2]
153     end
154
155     test "with valid `admin_token` query parameter, skips OAuth scopes check" do
156       clear_config([:admin_token], "password123")
157
158       build_conn()
159       |> get("/api/pleroma/admin/config?admin_token=password123")
160       |> json_response_and_validate_schema(200)
161     end
162   end
163
164   test "POST /api/pleroma/admin/config with configdb disabled", %{conn: conn} do
165     clear_config(:configurable_from_database, false)
166
167     conn =
168       conn
169       |> put_req_header("content-type", "application/json")
170       |> post("/api/pleroma/admin/config", %{"configs" => []})
171
172     assert json_response_and_validate_schema(conn, 400) ==
173              %{"error" => "You must enable configurable_from_database in your config file."}
174   end
175
176   describe "POST /api/pleroma/admin/config" do
177     setup do
178       http = Application.get_env(:pleroma, :http)
179
180       on_exit(fn ->
181         Application.delete_env(:pleroma, :key1)
182         Application.delete_env(:pleroma, :key2)
183         Application.delete_env(:pleroma, :key3)
184         Application.delete_env(:pleroma, :key4)
185         Application.delete_env(:pleroma, :keyaa1)
186         Application.delete_env(:pleroma, :keyaa2)
187         Application.delete_env(:pleroma, Pleroma.Web.Endpoint.NotReal)
188         Application.delete_env(:pleroma, Pleroma.Captcha.NotReal)
189         Application.put_env(:pleroma, :http, http)
190         Application.put_env(:tesla, :adapter, Tesla.Mock)
191         Restarter.Pleroma.refresh()
192       end)
193     end
194
195     setup do: clear_config(:configurable_from_database, true)
196
197     @tag capture_log: true
198     test "create new config setting in db", %{conn: conn} do
199       ueberauth = Application.get_env(:ueberauth, Ueberauth)
200       on_exit(fn -> Application.put_env(:ueberauth, Ueberauth, ueberauth) end)
201
202       conn =
203         conn
204         |> put_req_header("content-type", "application/json")
205         |> post("/api/pleroma/admin/config", %{
206           configs: [
207             %{group: ":pleroma", key: ":key1", value: "value1"},
208             %{
209               group: ":ueberauth",
210               key: "Ueberauth",
211               value: [%{"tuple" => [":consumer_secret", "aaaa"]}]
212             },
213             %{
214               group: ":pleroma",
215               key: ":key2",
216               value: %{
217                 ":nested_1" => "nested_value1",
218                 ":nested_2" => [
219                   %{":nested_22" => "nested_value222"},
220                   %{":nested_33" => %{":nested_44" => "nested_444"}}
221                 ]
222               }
223             },
224             %{
225               group: ":pleroma",
226               key: ":key3",
227               value: [
228                 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
229                 %{"nested_4" => true}
230               ]
231             },
232             %{
233               group: ":pleroma",
234               key: ":key4",
235               value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"}
236             },
237             %{
238               group: ":idna",
239               key: ":key5",
240               value: %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}
241             }
242           ]
243         })
244
245       assert json_response_and_validate_schema(conn, 200) == %{
246                "configs" => [
247                  %{
248                    "group" => ":pleroma",
249                    "key" => ":key1",
250                    "value" => "value1",
251                    "db" => [":key1"]
252                  },
253                  %{
254                    "group" => ":ueberauth",
255                    "key" => "Ueberauth",
256                    "value" => [%{"tuple" => [":consumer_secret", "aaaa"]}],
257                    "db" => [":consumer_secret"]
258                  },
259                  %{
260                    "group" => ":pleroma",
261                    "key" => ":key2",
262                    "value" => %{
263                      ":nested_1" => "nested_value1",
264                      ":nested_2" => [
265                        %{":nested_22" => "nested_value222"},
266                        %{":nested_33" => %{":nested_44" => "nested_444"}}
267                      ]
268                    },
269                    "db" => [":key2"]
270                  },
271                  %{
272                    "group" => ":pleroma",
273                    "key" => ":key3",
274                    "value" => [
275                      %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
276                      %{"nested_4" => true}
277                    ],
278                    "db" => [":key3"]
279                  },
280                  %{
281                    "group" => ":pleroma",
282                    "key" => ":key4",
283                    "value" => %{"endpoint" => "https://example.com", ":nested_5" => ":upload"},
284                    "db" => [":key4"]
285                  },
286                  %{
287                    "group" => ":idna",
288                    "key" => ":key5",
289                    "value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]},
290                    "db" => [":key5"]
291                  }
292                ],
293                "need_reboot" => false
294              }
295
296       assert Application.get_env(:pleroma, :key1) == "value1"
297
298       assert Application.get_env(:pleroma, :key2) == %{
299                nested_1: "nested_value1",
300                nested_2: [
301                  %{nested_22: "nested_value222"},
302                  %{nested_33: %{nested_44: "nested_444"}}
303                ]
304              }
305
306       assert Application.get_env(:pleroma, :key3) == [
307                %{"nested_3" => :nested_3, "nested_33" => "nested_33"},
308                %{"nested_4" => true}
309              ]
310
311       assert Application.get_env(:pleroma, :key4) == %{
312                "endpoint" => "https://example.com",
313                nested_5: :upload
314              }
315
316       assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []}
317     end
318
319     test "save configs setting without explicit key", %{conn: conn} do
320       adapter = Application.get_env(:http, :adapter)
321       send_user_agent = Application.get_env(:http, :send_user_agent)
322       user_agent = Application.get_env(:http, :user_agent)
323
324       on_exit(fn ->
325         Application.put_env(:http, :adapter, adapter)
326         Application.put_env(:http, :send_user_agent, send_user_agent)
327         Application.put_env(:http, :user_agent, user_agent)
328       end)
329
330       conn =
331         conn
332         |> put_req_header("content-type", "application/json")
333         |> post("/api/pleroma/admin/config", %{
334           configs: [
335             %{
336               group: ":http",
337               key: ":adapter",
338               value: [":someval"]
339             },
340             %{
341               group: ":http",
342               key: ":send_user_agent",
343               value: true
344             },
345             %{
346               group: ":http",
347               key: ":user_agent",
348               value: [":default"]
349             }
350           ]
351         })
352
353       assert json_response_and_validate_schema(conn, 200) == %{
354                "configs" => [
355                  %{
356                    "group" => ":http",
357                    "key" => ":adapter",
358                    "value" => [":someval"],
359                    "db" => [":adapter"]
360                  },
361                  %{
362                    "group" => ":http",
363                    "key" => ":send_user_agent",
364                    "value" => true,
365                    "db" => [":send_user_agent"]
366                  },
367                  %{
368                    "group" => ":http",
369                    "key" => ":user_agent",
370                    "value" => [":default"],
371                    "db" => [":user_agent"]
372                  }
373                ],
374                "need_reboot" => false
375              }
376
377       assert Application.get_env(:http, :adapter) == [:someval]
378       assert Application.get_env(:http, :send_user_agent) == true
379       assert Application.get_env(:http, :user_agent) == [:default]
380     end
381
382     test "saving config with partial update", %{conn: conn} do
383       insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: 2))
384
385       conn =
386         conn
387         |> put_req_header("content-type", "application/json")
388         |> post("/api/pleroma/admin/config", %{
389           configs: [
390             %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]}
391           ]
392         })
393
394       assert json_response_and_validate_schema(conn, 200) == %{
395                "configs" => [
396                  %{
397                    "group" => ":pleroma",
398                    "key" => ":key1",
399                    "value" => [
400                      %{"tuple" => [":key1", 1]},
401                      %{"tuple" => [":key2", 2]},
402                      %{"tuple" => [":key3", 3]}
403                    ],
404                    "db" => [":key1", ":key2", ":key3"]
405                  }
406                ],
407                "need_reboot" => false
408              }
409     end
410
411     test "saving config which need pleroma reboot", %{conn: conn} do
412       clear_config([:shout, :enabled], true)
413
414       assert conn
415              |> put_req_header("content-type", "application/json")
416              |> post(
417                "/api/pleroma/admin/config",
418                %{
419                  configs: [
420                    %{group: ":pleroma", key: ":shout", value: [%{"tuple" => [":enabled", true]}]}
421                  ]
422                }
423              )
424              |> json_response_and_validate_schema(200) == %{
425                "configs" => [
426                  %{
427                    "db" => [":enabled"],
428                    "group" => ":pleroma",
429                    "key" => ":shout",
430                    "value" => [%{"tuple" => [":enabled", true]}]
431                  }
432                ],
433                "need_reboot" => true
434              }
435
436       configs =
437         conn
438         |> get("/api/pleroma/admin/config")
439         |> json_response_and_validate_schema(200)
440
441       assert configs["need_reboot"]
442
443       capture_log(fn ->
444         assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) ==
445                  %{}
446       end) =~ "pleroma restarted"
447
448       configs =
449         conn
450         |> get("/api/pleroma/admin/config")
451         |> json_response_and_validate_schema(200)
452
453       assert configs["need_reboot"] == false
454     end
455
456     test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do
457       clear_config([:shout, :enabled], true)
458
459       assert conn
460              |> put_req_header("content-type", "application/json")
461              |> post(
462                "/api/pleroma/admin/config",
463                %{
464                  configs: [
465                    %{group: ":pleroma", key: ":shout", value: [%{"tuple" => [":enabled", true]}]}
466                  ]
467                }
468              )
469              |> json_response_and_validate_schema(200) == %{
470                "configs" => [
471                  %{
472                    "db" => [":enabled"],
473                    "group" => ":pleroma",
474                    "key" => ":shout",
475                    "value" => [%{"tuple" => [":enabled", true]}]
476                  }
477                ],
478                "need_reboot" => true
479              }
480
481       assert conn
482              |> put_req_header("content-type", "application/json")
483              |> post("/api/pleroma/admin/config", %{
484                configs: [
485                  %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]}
486                ]
487              })
488              |> json_response_and_validate_schema(200) == %{
489                "configs" => [
490                  %{
491                    "group" => ":pleroma",
492                    "key" => ":key1",
493                    "value" => [
494                      %{"tuple" => [":key3", 3]}
495                    ],
496                    "db" => [":key3"]
497                  }
498                ],
499                "need_reboot" => true
500              }
501
502       capture_log(fn ->
503         assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) ==
504                  %{}
505       end) =~ "pleroma restarted"
506
507       configs =
508         conn
509         |> get("/api/pleroma/admin/config")
510         |> json_response_and_validate_schema(200)
511
512       assert configs["need_reboot"] == false
513     end
514
515     test "saving config with nested merge", %{conn: conn} do
516       insert(:config, key: :key1, value: [key1: 1, key2: [k1: 1, k2: 2]])
517
518       conn =
519         conn
520         |> put_req_header("content-type", "application/json")
521         |> post("/api/pleroma/admin/config", %{
522           configs: [
523             %{
524               group: ":pleroma",
525               key: ":key1",
526               value: [
527                 %{"tuple" => [":key3", 3]},
528                 %{
529                   "tuple" => [
530                     ":key2",
531                     [
532                       %{"tuple" => [":k2", 1]},
533                       %{"tuple" => [":k3", 3]}
534                     ]
535                   ]
536                 }
537               ]
538             }
539           ]
540         })
541
542       assert json_response_and_validate_schema(conn, 200) == %{
543                "configs" => [
544                  %{
545                    "group" => ":pleroma",
546                    "key" => ":key1",
547                    "value" => [
548                      %{"tuple" => [":key1", 1]},
549                      %{"tuple" => [":key3", 3]},
550                      %{
551                        "tuple" => [
552                          ":key2",
553                          [
554                            %{"tuple" => [":k1", 1]},
555                            %{"tuple" => [":k2", 1]},
556                            %{"tuple" => [":k3", 3]}
557                          ]
558                        ]
559                      }
560                    ],
561                    "db" => [":key1", ":key3", ":key2"]
562                  }
563                ],
564                "need_reboot" => false
565              }
566     end
567
568     test "saving special atoms", %{conn: conn} do
569       conn =
570         conn
571         |> put_req_header("content-type", "application/json")
572         |> post("/api/pleroma/admin/config", %{
573           "configs" => [
574             %{
575               "group" => ":pleroma",
576               "key" => ":key1",
577               "value" => [
578                 %{
579                   "tuple" => [
580                     ":ssl_options",
581                     [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
582                   ]
583                 }
584               ]
585             }
586           ]
587         })
588
589       assert json_response_and_validate_schema(conn, 200) == %{
590                "configs" => [
591                  %{
592                    "group" => ":pleroma",
593                    "key" => ":key1",
594                    "value" => [
595                      %{
596                        "tuple" => [
597                          ":ssl_options",
598                          [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
599                        ]
600                      }
601                    ],
602                    "db" => [":ssl_options"]
603                  }
604                ],
605                "need_reboot" => false
606              }
607
608       assert Application.get_env(:pleroma, :key1) == [
609                ssl_options: [versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"]]
610              ]
611     end
612
613     test "saving full setting if value is in full_key_update list", %{conn: conn} do
614       backends = Application.get_env(:logger, :backends)
615       on_exit(fn -> Application.put_env(:logger, :backends, backends) end)
616
617       insert(:config,
618         group: :logger,
619         key: :backends,
620         value: []
621       )
622
623       Pleroma.Config.TransferTask.load_and_update_env([], false)
624
625       assert Application.get_env(:logger, :backends) == []
626
627       conn =
628         conn
629         |> put_req_header("content-type", "application/json")
630         |> post("/api/pleroma/admin/config", %{
631           configs: [
632             %{
633               group: ":logger",
634               key: ":backends",
635               value: [":console"]
636             }
637           ]
638         })
639
640       assert json_response_and_validate_schema(conn, 200) == %{
641                "configs" => [
642                  %{
643                    "group" => ":logger",
644                    "key" => ":backends",
645                    "value" => [
646                      ":console"
647                    ],
648                    "db" => [":backends"]
649                  }
650                ],
651                "need_reboot" => false
652              }
653
654       assert Application.get_env(:logger, :backends) == [
655                :console
656              ]
657     end
658
659     test "saving full setting if value is not keyword", %{conn: conn} do
660       insert(:config,
661         group: :tesla,
662         key: :adapter,
663         value: Tesla.Adapter.Hackey
664       )
665
666       conn =
667         conn
668         |> put_req_header("content-type", "application/json")
669         |> post("/api/pleroma/admin/config", %{
670           configs: [
671             %{group: ":tesla", key: ":adapter", value: "Tesla.Adapter.Httpc"}
672           ]
673         })
674
675       assert json_response_and_validate_schema(conn, 200) == %{
676                "configs" => [
677                  %{
678                    "group" => ":tesla",
679                    "key" => ":adapter",
680                    "value" => "Tesla.Adapter.Httpc",
681                    "db" => [":adapter"]
682                  }
683                ],
684                "need_reboot" => false
685              }
686     end
687
688     test "update config setting & delete with fallback to default value", %{
689       conn: conn,
690       admin: admin,
691       token: token
692     } do
693       ueberauth = Application.get_env(:ueberauth, Ueberauth)
694       insert(:config, key: :keyaa1)
695       insert(:config, key: :keyaa2)
696
697       config3 =
698         insert(:config,
699           group: :ueberauth,
700           key: Ueberauth
701         )
702
703       conn =
704         conn
705         |> put_req_header("content-type", "application/json")
706         |> post("/api/pleroma/admin/config", %{
707           configs: [
708             %{group: ":pleroma", key: ":keyaa1", value: "another_value"},
709             %{group: ":pleroma", key: ":keyaa2", value: "another_value"}
710           ]
711         })
712
713       assert json_response_and_validate_schema(conn, 200) == %{
714                "configs" => [
715                  %{
716                    "group" => ":pleroma",
717                    "key" => ":keyaa1",
718                    "value" => "another_value",
719                    "db" => [":keyaa1"]
720                  },
721                  %{
722                    "group" => ":pleroma",
723                    "key" => ":keyaa2",
724                    "value" => "another_value",
725                    "db" => [":keyaa2"]
726                  }
727                ],
728                "need_reboot" => false
729              }
730
731       assert Application.get_env(:pleroma, :keyaa1) == "another_value"
732       assert Application.get_env(:pleroma, :keyaa2) == "another_value"
733       assert Application.get_env(:ueberauth, Ueberauth) == config3.value
734
735       conn =
736         build_conn()
737         |> assign(:user, admin)
738         |> assign(:token, token)
739         |> put_req_header("content-type", "application/json")
740         |> post("/api/pleroma/admin/config", %{
741           configs: [
742             %{group: ":pleroma", key: ":keyaa2", delete: true},
743             %{
744               group: ":ueberauth",
745               key: "Ueberauth",
746               delete: true
747             }
748           ]
749         })
750
751       assert json_response_and_validate_schema(conn, 200) == %{
752                "configs" => [],
753                "need_reboot" => false
754              }
755
756       assert Application.get_env(:ueberauth, Ueberauth) == ueberauth
757       refute Keyword.has_key?(Application.get_all_env(:pleroma), :keyaa2)
758     end
759
760     test "common config example", %{conn: conn} do
761       conn =
762         conn
763         |> put_req_header("content-type", "application/json")
764         |> post("/api/pleroma/admin/config", %{
765           configs: [
766             %{
767               "group" => ":pleroma",
768               "key" => "Pleroma.Captcha.NotReal",
769               "value" => [
770                 %{"tuple" => [":enabled", false]},
771                 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
772                 %{"tuple" => [":seconds_valid", 60]},
773                 %{"tuple" => [":path", ""]},
774                 %{"tuple" => [":key1", nil]},
775                 %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
776                 %{"tuple" => [":regex1", "~r/https:\/\/example.com/"]},
777                 %{"tuple" => [":regex2", "~r/https:\/\/example.com/u"]},
778                 %{"tuple" => [":regex3", "~r/https:\/\/example.com/i"]},
779                 %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]},
780                 %{"tuple" => [":name", "Pleroma"]}
781               ]
782             }
783           ]
784         })
785
786       assert Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma"
787
788       assert json_response_and_validate_schema(conn, 200) == %{
789                "configs" => [
790                  %{
791                    "group" => ":pleroma",
792                    "key" => "Pleroma.Captcha.NotReal",
793                    "value" => [
794                      %{"tuple" => [":enabled", false]},
795                      %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
796                      %{"tuple" => [":seconds_valid", 60]},
797                      %{"tuple" => [":path", ""]},
798                      %{"tuple" => [":key1", nil]},
799                      %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
800                      %{"tuple" => [":regex1", "~r/https:\\/\\/example.com/"]},
801                      %{"tuple" => [":regex2", "~r/https:\\/\\/example.com/u"]},
802                      %{"tuple" => [":regex3", "~r/https:\\/\\/example.com/i"]},
803                      %{"tuple" => [":regex4", "~r/https:\\/\\/example.com/s"]},
804                      %{"tuple" => [":name", "Pleroma"]}
805                    ],
806                    "db" => [
807                      ":enabled",
808                      ":method",
809                      ":seconds_valid",
810                      ":path",
811                      ":key1",
812                      ":partial_chain",
813                      ":regex1",
814                      ":regex2",
815                      ":regex3",
816                      ":regex4",
817                      ":name"
818                    ]
819                  }
820                ],
821                "need_reboot" => false
822              }
823     end
824
825     test "tuples with more than two values", %{conn: conn} do
826       conn =
827         conn
828         |> put_req_header("content-type", "application/json")
829         |> post("/api/pleroma/admin/config", %{
830           configs: [
831             %{
832               "group" => ":pleroma",
833               "key" => "Pleroma.Web.Endpoint.NotReal",
834               "value" => [
835                 %{
836                   "tuple" => [
837                     ":http",
838                     [
839                       %{
840                         "tuple" => [
841                           ":key2",
842                           [
843                             %{
844                               "tuple" => [
845                                 ":_",
846                                 [
847                                   %{
848                                     "tuple" => [
849                                       "/api/v1/streaming",
850                                       "Pleroma.Web.MastodonAPI.WebsocketHandler",
851                                       []
852                                     ]
853                                   },
854                                   %{
855                                     "tuple" => [
856                                       "/websocket",
857                                       "Phoenix.Endpoint.CowboyWebSocket",
858                                       %{
859                                         "tuple" => [
860                                           "Phoenix.Transports.WebSocket",
861                                           %{
862                                             "tuple" => [
863                                               "Pleroma.Web.Endpoint",
864                                               "Pleroma.Web.UserSocket",
865                                               []
866                                             ]
867                                           }
868                                         ]
869                                       }
870                                     ]
871                                   },
872                                   %{
873                                     "tuple" => [
874                                       ":_",
875                                       "Phoenix.Endpoint.Cowboy2Handler",
876                                       %{"tuple" => ["Pleroma.Web.Endpoint", []]}
877                                     ]
878                                   }
879                                 ]
880                               ]
881                             }
882                           ]
883                         ]
884                       }
885                     ]
886                   ]
887                 }
888               ]
889             }
890           ]
891         })
892
893       assert json_response_and_validate_schema(conn, 200) == %{
894                "configs" => [
895                  %{
896                    "group" => ":pleroma",
897                    "key" => "Pleroma.Web.Endpoint.NotReal",
898                    "value" => [
899                      %{
900                        "tuple" => [
901                          ":http",
902                          [
903                            %{
904                              "tuple" => [
905                                ":key2",
906                                [
907                                  %{
908                                    "tuple" => [
909                                      ":_",
910                                      [
911                                        %{
912                                          "tuple" => [
913                                            "/api/v1/streaming",
914                                            "Pleroma.Web.MastodonAPI.WebsocketHandler",
915                                            []
916                                          ]
917                                        },
918                                        %{
919                                          "tuple" => [
920                                            "/websocket",
921                                            "Phoenix.Endpoint.CowboyWebSocket",
922                                            %{
923                                              "tuple" => [
924                                                "Phoenix.Transports.WebSocket",
925                                                %{
926                                                  "tuple" => [
927                                                    "Pleroma.Web.Endpoint",
928                                                    "Pleroma.Web.UserSocket",
929                                                    []
930                                                  ]
931                                                }
932                                              ]
933                                            }
934                                          ]
935                                        },
936                                        %{
937                                          "tuple" => [
938                                            ":_",
939                                            "Phoenix.Endpoint.Cowboy2Handler",
940                                            %{"tuple" => ["Pleroma.Web.Endpoint", []]}
941                                          ]
942                                        }
943                                      ]
944                                    ]
945                                  }
946                                ]
947                              ]
948                            }
949                          ]
950                        ]
951                      }
952                    ],
953                    "db" => [":http"]
954                  }
955                ],
956                "need_reboot" => false
957              }
958     end
959
960     test "settings with nesting map", %{conn: conn} do
961       conn =
962         conn
963         |> put_req_header("content-type", "application/json")
964         |> post("/api/pleroma/admin/config", %{
965           configs: [
966             %{
967               "group" => ":pleroma",
968               "key" => ":key1",
969               "value" => [
970                 %{"tuple" => [":key2", "some_val"]},
971                 %{
972                   "tuple" => [
973                     ":key3",
974                     %{
975                       ":max_options" => 20,
976                       ":max_option_chars" => 200,
977                       ":min_expiration" => 0,
978                       ":max_expiration" => 31_536_000,
979                       "nested" => %{
980                         ":max_options" => 20,
981                         ":max_option_chars" => 200,
982                         ":min_expiration" => 0,
983                         ":max_expiration" => 31_536_000
984                       }
985                     }
986                   ]
987                 }
988               ]
989             }
990           ]
991         })
992
993       assert json_response_and_validate_schema(conn, 200) ==
994                %{
995                  "configs" => [
996                    %{
997                      "group" => ":pleroma",
998                      "key" => ":key1",
999                      "value" => [
1000                        %{"tuple" => [":key2", "some_val"]},
1001                        %{
1002                          "tuple" => [
1003                            ":key3",
1004                            %{
1005                              ":max_expiration" => 31_536_000,
1006                              ":max_option_chars" => 200,
1007                              ":max_options" => 20,
1008                              ":min_expiration" => 0,
1009                              "nested" => %{
1010                                ":max_expiration" => 31_536_000,
1011                                ":max_option_chars" => 200,
1012                                ":max_options" => 20,
1013                                ":min_expiration" => 0
1014                              }
1015                            }
1016                          ]
1017                        }
1018                      ],
1019                      "db" => [":key2", ":key3"]
1020                    }
1021                  ],
1022                  "need_reboot" => false
1023                }
1024     end
1025
1026     test "value as map", %{conn: conn} do
1027       conn =
1028         conn
1029         |> put_req_header("content-type", "application/json")
1030         |> post("/api/pleroma/admin/config", %{
1031           configs: [
1032             %{
1033               "group" => ":pleroma",
1034               "key" => ":key1",
1035               "value" => %{"key" => "some_val"}
1036             }
1037           ]
1038         })
1039
1040       assert json_response_and_validate_schema(conn, 200) ==
1041                %{
1042                  "configs" => [
1043                    %{
1044                      "group" => ":pleroma",
1045                      "key" => ":key1",
1046                      "value" => %{"key" => "some_val"},
1047                      "db" => [":key1"]
1048                    }
1049                  ],
1050                  "need_reboot" => false
1051                }
1052     end
1053
1054     test "queues key as atom", %{conn: conn} do
1055       conn =
1056         conn
1057         |> put_req_header("content-type", "application/json")
1058         |> post("/api/pleroma/admin/config", %{
1059           configs: [
1060             %{
1061               "group" => ":oban",
1062               "key" => ":queues",
1063               "value" => [
1064                 %{"tuple" => [":federator_incoming", 50]},
1065                 %{"tuple" => [":federator_outgoing", 50]},
1066                 %{"tuple" => [":web_push", 50]},
1067                 %{"tuple" => [":mailer", 10]},
1068                 %{"tuple" => [":transmogrifier", 20]},
1069                 %{"tuple" => [":scheduled_activities", 10]},
1070                 %{"tuple" => [":background", 5]}
1071               ]
1072             }
1073           ]
1074         })
1075
1076       assert json_response_and_validate_schema(conn, 200) == %{
1077                "configs" => [
1078                  %{
1079                    "group" => ":oban",
1080                    "key" => ":queues",
1081                    "value" => [
1082                      %{"tuple" => [":federator_incoming", 50]},
1083                      %{"tuple" => [":federator_outgoing", 50]},
1084                      %{"tuple" => [":web_push", 50]},
1085                      %{"tuple" => [":mailer", 10]},
1086                      %{"tuple" => [":transmogrifier", 20]},
1087                      %{"tuple" => [":scheduled_activities", 10]},
1088                      %{"tuple" => [":background", 5]}
1089                    ],
1090                    "db" => [
1091                      ":federator_incoming",
1092                      ":federator_outgoing",
1093                      ":web_push",
1094                      ":mailer",
1095                      ":transmogrifier",
1096                      ":scheduled_activities",
1097                      ":background"
1098                    ]
1099                  }
1100                ],
1101                "need_reboot" => false
1102              }
1103     end
1104
1105     test "delete part of settings by atom subkeys", %{conn: conn} do
1106       insert(:config,
1107         key: :keyaa1,
1108         value: [subkey1: "val1", subkey2: "val2", subkey3: "val3"]
1109       )
1110
1111       conn =
1112         conn
1113         |> put_req_header("content-type", "application/json")
1114         |> post("/api/pleroma/admin/config", %{
1115           configs: [
1116             %{
1117               group: ":pleroma",
1118               key: ":keyaa1",
1119               subkeys: [":subkey1", ":subkey3"],
1120               delete: true
1121             }
1122           ]
1123         })
1124
1125       assert json_response_and_validate_schema(conn, 200) == %{
1126                "configs" => [
1127                  %{
1128                    "group" => ":pleroma",
1129                    "key" => ":keyaa1",
1130                    "value" => [%{"tuple" => [":subkey2", "val2"]}],
1131                    "db" => [":subkey2"]
1132                  }
1133                ],
1134                "need_reboot" => false
1135              }
1136     end
1137
1138     test "proxy tuple localhost", %{conn: conn} do
1139       conn =
1140         conn
1141         |> put_req_header("content-type", "application/json")
1142         |> post("/api/pleroma/admin/config", %{
1143           configs: [
1144             %{
1145               group: ":pleroma",
1146               key: ":http",
1147               value: [
1148                 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]}
1149               ]
1150             }
1151           ]
1152         })
1153
1154       assert %{
1155                "configs" => [
1156                  %{
1157                    "group" => ":pleroma",
1158                    "key" => ":http",
1159                    "value" => value,
1160                    "db" => db
1161                  }
1162                ]
1163              } = json_response_and_validate_schema(conn, 200)
1164
1165       assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]} in value
1166       assert ":proxy_url" in db
1167     end
1168
1169     test "proxy tuple domain", %{conn: conn} do
1170       conn =
1171         conn
1172         |> put_req_header("content-type", "application/json")
1173         |> post("/api/pleroma/admin/config", %{
1174           configs: [
1175             %{
1176               group: ":pleroma",
1177               key: ":http",
1178               value: [
1179                 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]}
1180               ]
1181             }
1182           ]
1183         })
1184
1185       assert %{
1186                "configs" => [
1187                  %{
1188                    "group" => ":pleroma",
1189                    "key" => ":http",
1190                    "value" => value,
1191                    "db" => db
1192                  }
1193                ]
1194              } = json_response_and_validate_schema(conn, 200)
1195
1196       assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]} in value
1197       assert ":proxy_url" in db
1198     end
1199
1200     test "proxy tuple ip", %{conn: conn} do
1201       conn =
1202         conn
1203         |> put_req_header("content-type", "application/json")
1204         |> post("/api/pleroma/admin/config", %{
1205           configs: [
1206             %{
1207               group: ":pleroma",
1208               key: ":http",
1209               value: [
1210                 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]}
1211               ]
1212             }
1213           ]
1214         })
1215
1216       assert %{
1217                "configs" => [
1218                  %{
1219                    "group" => ":pleroma",
1220                    "key" => ":http",
1221                    "value" => value,
1222                    "db" => db
1223                  }
1224                ]
1225              } = json_response_and_validate_schema(conn, 200)
1226
1227       assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]} in value
1228       assert ":proxy_url" in db
1229     end
1230
1231     @tag capture_log: true
1232     test "doesn't set keys not in the whitelist", %{conn: conn} do
1233       clear_config(:database_config_whitelist, [
1234         {:pleroma, :key1},
1235         {:pleroma, :key2},
1236         {:pleroma, Pleroma.Captcha.NotReal},
1237         {:not_real}
1238       ])
1239
1240       conn
1241       |> put_req_header("content-type", "application/json")
1242       |> post("/api/pleroma/admin/config", %{
1243         configs: [
1244           %{group: ":pleroma", key: ":key1", value: "value1"},
1245           %{group: ":pleroma", key: ":key2", value: "value2"},
1246           %{group: ":pleroma", key: ":key3", value: "value3"},
1247           %{group: ":pleroma", key: "Pleroma.Web.Endpoint.NotReal", value: "value4"},
1248           %{group: ":pleroma", key: "Pleroma.Captcha.NotReal", value: "value5"},
1249           %{group: ":not_real", key: ":anything", value: "value6"}
1250         ]
1251       })
1252
1253       assert Application.get_env(:pleroma, :key1) == "value1"
1254       assert Application.get_env(:pleroma, :key2) == "value2"
1255       assert Application.get_env(:pleroma, :key3) == nil
1256       assert Application.get_env(:pleroma, Pleroma.Web.Endpoint.NotReal) == nil
1257       assert Application.get_env(:pleroma, Pleroma.Captcha.NotReal) == "value5"
1258       assert Application.get_env(:not_real, :anything) == "value6"
1259     end
1260
1261     test "args for Pleroma.Upload.Filter.Mogrify with custom tuples", %{conn: conn} do
1262       clear_config(Pleroma.Upload.Filter.Mogrify)
1263
1264       assert conn
1265              |> put_req_header("content-type", "application/json")
1266              |> post("/api/pleroma/admin/config", %{
1267                configs: [
1268                  %{
1269                    group: ":pleroma",
1270                    key: "Pleroma.Upload.Filter.Mogrify",
1271                    value: [
1272                      %{"tuple" => [":args", ["auto-orient", "strip"]]}
1273                    ]
1274                  }
1275                ]
1276              })
1277              |> json_response_and_validate_schema(200) == %{
1278                "configs" => [
1279                  %{
1280                    "group" => ":pleroma",
1281                    "key" => "Pleroma.Upload.Filter.Mogrify",
1282                    "value" => [
1283                      %{"tuple" => [":args", ["auto-orient", "strip"]]}
1284                    ],
1285                    "db" => [":args"]
1286                  }
1287                ],
1288                "need_reboot" => false
1289              }
1290
1291       assert Config.get(Pleroma.Upload.Filter.Mogrify) == [args: ["auto-orient", "strip"]]
1292
1293       assert conn
1294              |> put_req_header("content-type", "application/json")
1295              |> post("/api/pleroma/admin/config", %{
1296                configs: [
1297                  %{
1298                    group: ":pleroma",
1299                    key: "Pleroma.Upload.Filter.Mogrify",
1300                    value: [
1301                      %{
1302                        "tuple" => [
1303                          ":args",
1304                          [
1305                            "auto-orient",
1306                            "strip",
1307                            "{\"implode\", \"1\"}",
1308                            "{\"resize\", \"3840x1080>\"}"
1309                          ]
1310                        ]
1311                      }
1312                    ]
1313                  }
1314                ]
1315              })
1316              |> json_response(200) == %{
1317                "configs" => [
1318                  %{
1319                    "group" => ":pleroma",
1320                    "key" => "Pleroma.Upload.Filter.Mogrify",
1321                    "value" => [
1322                      %{
1323                        "tuple" => [
1324                          ":args",
1325                          [
1326                            "auto-orient",
1327                            "strip",
1328                            "{\"implode\", \"1\"}",
1329                            "{\"resize\", \"3840x1080>\"}"
1330                          ]
1331                        ]
1332                      }
1333                    ],
1334                    "db" => [":args"]
1335                  }
1336                ],
1337                "need_reboot" => false
1338              }
1339
1340       assert Config.get(Pleroma.Upload.Filter.Mogrify) == [
1341                args: ["auto-orient", "strip", {"implode", "1"}, {"resize", "3840x1080>"}]
1342              ]
1343     end
1344
1345     test "enables the welcome messages", %{conn: conn} do
1346       clear_config([:welcome])
1347
1348       params = %{
1349         "group" => ":pleroma",
1350         "key" => ":welcome",
1351         "value" => [
1352           %{
1353             "tuple" => [
1354               ":direct_message",
1355               [
1356                 %{"tuple" => [":enabled", true]},
1357                 %{"tuple" => [":message", "Welcome to Pleroma!"]},
1358                 %{"tuple" => [":sender_nickname", "pleroma"]}
1359               ]
1360             ]
1361           },
1362           %{
1363             "tuple" => [
1364               ":chat_message",
1365               [
1366                 %{"tuple" => [":enabled", true]},
1367                 %{"tuple" => [":message", "Welcome to Pleroma!"]},
1368                 %{"tuple" => [":sender_nickname", "pleroma"]}
1369               ]
1370             ]
1371           },
1372           %{
1373             "tuple" => [
1374               ":email",
1375               [
1376                 %{"tuple" => [":enabled", true]},
1377                 %{"tuple" => [":sender", %{"tuple" => ["pleroma@dev.dev", "Pleroma"]}]},
1378                 %{"tuple" => [":subject", "Welcome to <%= instance_name %>!"]},
1379                 %{"tuple" => [":html", "Welcome to <%= instance_name %>!"]},
1380                 %{"tuple" => [":text", "Welcome to <%= instance_name %>!"]}
1381               ]
1382             ]
1383           }
1384         ]
1385       }
1386
1387       refute Pleroma.User.WelcomeEmail.enabled?()
1388       refute Pleroma.User.WelcomeMessage.enabled?()
1389       refute Pleroma.User.WelcomeChatMessage.enabled?()
1390
1391       res =
1392         assert conn
1393                |> put_req_header("content-type", "application/json")
1394                |> post("/api/pleroma/admin/config", %{"configs" => [params]})
1395                |> json_response_and_validate_schema(200)
1396
1397       assert Pleroma.User.WelcomeEmail.enabled?()
1398       assert Pleroma.User.WelcomeMessage.enabled?()
1399       assert Pleroma.User.WelcomeChatMessage.enabled?()
1400
1401       assert res == %{
1402                "configs" => [
1403                  %{
1404                    "db" => [":direct_message", ":chat_message", ":email"],
1405                    "group" => ":pleroma",
1406                    "key" => ":welcome",
1407                    "value" => params["value"]
1408                  }
1409                ],
1410                "need_reboot" => false
1411              }
1412     end
1413
1414     test "custom instance thumbnail", %{conn: conn} do
1415       clear_config([:instance])
1416
1417       params = %{
1418         "group" => ":pleroma",
1419         "key" => ":instance",
1420         "value" => [
1421           %{
1422             "tuple" => [
1423               ":instance_thumbnail",
1424               "https://example.com/media/new_thumbnail.jpg"
1425             ]
1426           }
1427         ]
1428       }
1429
1430       assert conn
1431              |> put_req_header("content-type", "application/json")
1432              |> post("/api/pleroma/admin/config", %{"configs" => [params]})
1433              |> json_response_and_validate_schema(200) ==
1434                %{
1435                  "configs" => [
1436                    %{
1437                      "db" => [":instance_thumbnail"],
1438                      "group" => ":pleroma",
1439                      "key" => ":instance",
1440                      "value" => params["value"]
1441                    }
1442                  ],
1443                  "need_reboot" => false
1444                }
1445
1446       assert conn
1447              |> get("/api/v1/instance")
1448              |> json_response_and_validate_schema(200)
1449              |> Map.take(["thumbnail"]) ==
1450                %{"thumbnail" => "https://example.com/media/new_thumbnail.jpg"}
1451     end
1452
1453     test "Concurrent Limiter", %{conn: conn} do
1454       clear_config([ConcurrentLimiter])
1455
1456       params = %{
1457         "group" => ":pleroma",
1458         "key" => "ConcurrentLimiter",
1459         "value" => [
1460           %{
1461             "tuple" => [
1462               "Pleroma.Web.RichMedia.Helpers",
1463               [
1464                 %{"tuple" => [":max_running", 6]},
1465                 %{"tuple" => [":max_waiting", 6]}
1466               ]
1467             ]
1468           },
1469           %{
1470             "tuple" => [
1471               "Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy",
1472               [
1473                 %{"tuple" => [":max_running", 7]},
1474                 %{"tuple" => [":max_waiting", 7]}
1475               ]
1476             ]
1477           }
1478         ]
1479       }
1480
1481       assert conn
1482              |> put_req_header("content-type", "application/json")
1483              |> post("/api/pleroma/admin/config", %{"configs" => [params]})
1484              |> json_response_and_validate_schema(200)
1485     end
1486   end
1487
1488   describe "GET /api/pleroma/admin/config/descriptions" do
1489     test "structure", %{conn: conn} do
1490       conn = get(conn, "/api/pleroma/admin/config/descriptions")
1491
1492       assert [child | _others] = json_response_and_validate_schema(conn, 200)
1493
1494       assert child["children"]
1495       assert child["key"]
1496       assert String.starts_with?(child["group"], ":")
1497       assert child["description"]
1498     end
1499
1500     test "filters by database configuration whitelist", %{conn: conn} do
1501       clear_config(:database_config_whitelist, [
1502         {:pleroma, :instance},
1503         {:pleroma, :activitypub},
1504         {:pleroma, Pleroma.Upload},
1505         {:esshd}
1506       ])
1507
1508       conn = get(conn, "/api/pleroma/admin/config/descriptions")
1509
1510       children = json_response_and_validate_schema(conn, 200)
1511
1512       assert length(children) == 4
1513
1514       assert Enum.count(children, fn c -> c["group"] == ":pleroma" end) == 3
1515
1516       instance = Enum.find(children, fn c -> c["key"] == ":instance" end)
1517       assert instance["children"]
1518
1519       activitypub = Enum.find(children, fn c -> c["key"] == ":activitypub" end)
1520       assert activitypub["children"]
1521
1522       web_endpoint = Enum.find(children, fn c -> c["key"] == "Pleroma.Upload" end)
1523       assert web_endpoint["children"]
1524
1525       esshd = Enum.find(children, fn c -> c["group"] == ":esshd" end)
1526       assert esshd["children"]
1527     end
1528   end
1529 end