total rebase
[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     @tag capture_log: true
320     test "save configs setting without explicit key", %{conn: conn} do
321       adapter = Application.get_env(:http, :adapter)
322       send_user_agent = Application.get_env(:http, :send_user_agent)
323       user_agent = Application.get_env(:http, :user_agent)
324
325       on_exit(fn ->
326         Application.put_env(:http, :adapter, adapter)
327         Application.put_env(:http, :send_user_agent, send_user_agent)
328         Application.put_env(:http, :user_agent, user_agent)
329       end)
330
331       conn =
332         conn
333         |> put_req_header("content-type", "application/json")
334         |> post("/api/pleroma/admin/config", %{
335           configs: [
336             %{
337               group: ":http",
338               key: ":adapter",
339               value: [":someval"]
340             },
341             %{
342               group: ":http",
343               key: ":send_user_agent",
344               value: true
345             },
346             %{
347               group: ":http",
348               key: ":user_agent",
349               value: [":default"]
350             }
351           ]
352         })
353
354       assert json_response_and_validate_schema(conn, 200) == %{
355                "configs" => [
356                  %{
357                    "group" => ":http",
358                    "key" => ":adapter",
359                    "value" => [":someval"],
360                    "db" => [":adapter"]
361                  },
362                  %{
363                    "group" => ":http",
364                    "key" => ":send_user_agent",
365                    "value" => true,
366                    "db" => [":send_user_agent"]
367                  },
368                  %{
369                    "group" => ":http",
370                    "key" => ":user_agent",
371                    "value" => [":default"],
372                    "db" => [":user_agent"]
373                  }
374                ],
375                "need_reboot" => false
376              }
377
378       assert Application.get_env(:http, :adapter) == [:someval]
379       assert Application.get_env(:http, :send_user_agent) == true
380       assert Application.get_env(:http, :user_agent) == [:default]
381     end
382
383     test "saving config with partial update", %{conn: conn} do
384       insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: 2))
385
386       conn =
387         conn
388         |> put_req_header("content-type", "application/json")
389         |> post("/api/pleroma/admin/config", %{
390           configs: [
391             %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]}
392           ]
393         })
394
395       assert json_response_and_validate_schema(conn, 200) == %{
396                "configs" => [
397                  %{
398                    "group" => ":pleroma",
399                    "key" => ":key1",
400                    "value" => [
401                      %{"tuple" => [":key1", 1]},
402                      %{"tuple" => [":key2", 2]},
403                      %{"tuple" => [":key3", 3]}
404                    ],
405                    "db" => [":key1", ":key2", ":key3"]
406                  }
407                ],
408                "need_reboot" => false
409              }
410     end
411
412     test "saving config which need pleroma reboot", %{conn: conn} do
413       clear_config([:shout, :enabled], true)
414
415       assert conn
416              |> put_req_header("content-type", "application/json")
417              |> post(
418                "/api/pleroma/admin/config",
419                %{
420                  configs: [
421                    %{group: ":pleroma", key: ":shout", value: [%{"tuple" => [":enabled", true]}]}
422                  ]
423                }
424              )
425              |> json_response_and_validate_schema(200) == %{
426                "configs" => [
427                  %{
428                    "db" => [":enabled"],
429                    "group" => ":pleroma",
430                    "key" => ":shout",
431                    "value" => [%{"tuple" => [":enabled", true]}]
432                  }
433                ],
434                "need_reboot" => true
435              }
436
437       configs =
438         conn
439         |> get("/api/pleroma/admin/config")
440         |> json_response_and_validate_schema(200)
441
442       assert configs["need_reboot"]
443
444       capture_log(fn ->
445         assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) ==
446                  %{}
447       end) =~ "pleroma restarted"
448
449       configs =
450         conn
451         |> get("/api/pleroma/admin/config")
452         |> json_response_and_validate_schema(200)
453
454       assert configs["need_reboot"] == false
455     end
456
457     test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do
458       clear_config([:shout, :enabled], true)
459
460       assert conn
461              |> put_req_header("content-type", "application/json")
462              |> post(
463                "/api/pleroma/admin/config",
464                %{
465                  configs: [
466                    %{group: ":pleroma", key: ":shout", value: [%{"tuple" => [":enabled", true]}]}
467                  ]
468                }
469              )
470              |> json_response_and_validate_schema(200) == %{
471                "configs" => [
472                  %{
473                    "db" => [":enabled"],
474                    "group" => ":pleroma",
475                    "key" => ":shout",
476                    "value" => [%{"tuple" => [":enabled", true]}]
477                  }
478                ],
479                "need_reboot" => true
480              }
481
482       assert conn
483              |> put_req_header("content-type", "application/json")
484              |> post("/api/pleroma/admin/config", %{
485                configs: [
486                  %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]}
487                ]
488              })
489              |> json_response_and_validate_schema(200) == %{
490                "configs" => [
491                  %{
492                    "group" => ":pleroma",
493                    "key" => ":key1",
494                    "value" => [
495                      %{"tuple" => [":key3", 3]}
496                    ],
497                    "db" => [":key3"]
498                  }
499                ],
500                "need_reboot" => true
501              }
502
503       capture_log(fn ->
504         assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) ==
505                  %{}
506       end) =~ "pleroma restarted"
507
508       configs =
509         conn
510         |> get("/api/pleroma/admin/config")
511         |> json_response_and_validate_schema(200)
512
513       assert configs["need_reboot"] == false
514     end
515
516     test "saving config with nested merge", %{conn: conn} do
517       insert(:config, key: :key1, value: [key1: 1, key2: [k1: 1, k2: 2]])
518
519       conn =
520         conn
521         |> put_req_header("content-type", "application/json")
522         |> post("/api/pleroma/admin/config", %{
523           configs: [
524             %{
525               group: ":pleroma",
526               key: ":key1",
527               value: [
528                 %{"tuple" => [":key3", 3]},
529                 %{
530                   "tuple" => [
531                     ":key2",
532                     [
533                       %{"tuple" => [":k2", 1]},
534                       %{"tuple" => [":k3", 3]}
535                     ]
536                   ]
537                 }
538               ]
539             }
540           ]
541         })
542
543       assert json_response_and_validate_schema(conn, 200) == %{
544                "configs" => [
545                  %{
546                    "group" => ":pleroma",
547                    "key" => ":key1",
548                    "value" => [
549                      %{"tuple" => [":key1", 1]},
550                      %{"tuple" => [":key3", 3]},
551                      %{
552                        "tuple" => [
553                          ":key2",
554                          [
555                            %{"tuple" => [":k1", 1]},
556                            %{"tuple" => [":k2", 1]},
557                            %{"tuple" => [":k3", 3]}
558                          ]
559                        ]
560                      }
561                    ],
562                    "db" => [":key1", ":key3", ":key2"]
563                  }
564                ],
565                "need_reboot" => false
566              }
567     end
568
569     test "saving special atoms", %{conn: conn} do
570       conn =
571         conn
572         |> put_req_header("content-type", "application/json")
573         |> post("/api/pleroma/admin/config", %{
574           "configs" => [
575             %{
576               "group" => ":pleroma",
577               "key" => ":key1",
578               "value" => [
579                 %{
580                   "tuple" => [
581                     ":ssl_options",
582                     [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
583                   ]
584                 }
585               ]
586             }
587           ]
588         })
589
590       assert json_response_and_validate_schema(conn, 200) == %{
591                "configs" => [
592                  %{
593                    "group" => ":pleroma",
594                    "key" => ":key1",
595                    "value" => [
596                      %{
597                        "tuple" => [
598                          ":ssl_options",
599                          [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
600                        ]
601                      }
602                    ],
603                    "db" => [":ssl_options"]
604                  }
605                ],
606                "need_reboot" => false
607              }
608
609       assert Application.get_env(:pleroma, :key1) == [
610                ssl_options: [versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"]]
611              ]
612     end
613
614     test "saving full setting if value is in full_key_update list", %{conn: conn} do
615       backends = Application.get_env(:logger, :backends)
616       on_exit(fn -> Application.put_env(:logger, :backends, backends) end)
617
618       insert(:config,
619         group: :logger,
620         key: :backends,
621         value: []
622       )
623
624       Pleroma.Config.TransferTask.load_and_update_env([], false)
625
626       assert Application.get_env(:logger, :backends) == []
627
628       conn =
629         conn
630         |> put_req_header("content-type", "application/json")
631         |> post("/api/pleroma/admin/config", %{
632           configs: [
633             %{
634               group: ":logger",
635               key: ":backends",
636               value: [":console"]
637             }
638           ]
639         })
640
641       assert json_response_and_validate_schema(conn, 200) == %{
642                "configs" => [
643                  %{
644                    "group" => ":logger",
645                    "key" => ":backends",
646                    "value" => [
647                      ":console"
648                    ],
649                    "db" => [":backends"]
650                  }
651                ],
652                "need_reboot" => false
653              }
654
655       assert Application.get_env(:logger, :backends) == [
656                :console
657              ]
658     end
659
660     test "saving full setting if value is not keyword", %{conn: conn} do
661       insert(:config,
662         group: :tesla,
663         key: :adapter,
664         value: Tesla.Adapter.Hackey
665       )
666
667       conn =
668         conn
669         |> put_req_header("content-type", "application/json")
670         |> post("/api/pleroma/admin/config", %{
671           configs: [
672             %{group: ":tesla", key: ":adapter", value: "Tesla.Adapter.Httpc"}
673           ]
674         })
675
676       assert json_response_and_validate_schema(conn, 200) == %{
677                "configs" => [
678                  %{
679                    "group" => ":tesla",
680                    "key" => ":adapter",
681                    "value" => "Tesla.Adapter.Httpc",
682                    "db" => [":adapter"]
683                  }
684                ],
685                "need_reboot" => false
686              }
687     end
688
689     test "update config setting & delete with fallback to default value", %{
690       conn: conn,
691       admin: admin,
692       token: token
693     } do
694       ueberauth = Application.get_env(:ueberauth, Ueberauth)
695       insert(:config, key: :keyaa1)
696       insert(:config, key: :keyaa2)
697
698       config3 =
699         insert(:config,
700           group: :ueberauth,
701           key: Ueberauth
702         )
703
704       conn =
705         conn
706         |> put_req_header("content-type", "application/json")
707         |> post("/api/pleroma/admin/config", %{
708           configs: [
709             %{group: ":pleroma", key: ":keyaa1", value: "another_value"},
710             %{group: ":pleroma", key: ":keyaa2", value: "another_value"}
711           ]
712         })
713
714       assert json_response_and_validate_schema(conn, 200) == %{
715                "configs" => [
716                  %{
717                    "group" => ":pleroma",
718                    "key" => ":keyaa1",
719                    "value" => "another_value",
720                    "db" => [":keyaa1"]
721                  },
722                  %{
723                    "group" => ":pleroma",
724                    "key" => ":keyaa2",
725                    "value" => "another_value",
726                    "db" => [":keyaa2"]
727                  }
728                ],
729                "need_reboot" => false
730              }
731
732       assert Application.get_env(:pleroma, :keyaa1) == "another_value"
733       assert Application.get_env(:pleroma, :keyaa2) == "another_value"
734       assert Application.get_env(:ueberauth, Ueberauth) == config3.value
735
736       conn =
737         build_conn()
738         |> assign(:user, admin)
739         |> assign(:token, token)
740         |> put_req_header("content-type", "application/json")
741         |> post("/api/pleroma/admin/config", %{
742           configs: [
743             %{group: ":pleroma", key: ":keyaa2", delete: true},
744             %{
745               group: ":ueberauth",
746               key: "Ueberauth",
747               delete: true
748             }
749           ]
750         })
751
752       assert json_response_and_validate_schema(conn, 200) == %{
753                "configs" => [],
754                "need_reboot" => false
755              }
756
757       assert Application.get_env(:ueberauth, Ueberauth) == ueberauth
758       refute Keyword.has_key?(Application.get_all_env(:pleroma), :keyaa2)
759     end
760
761     test "common config example", %{conn: conn} do
762       conn =
763         conn
764         |> put_req_header("content-type", "application/json")
765         |> post("/api/pleroma/admin/config", %{
766           configs: [
767             %{
768               "group" => ":pleroma",
769               "key" => "Pleroma.Captcha.NotReal",
770               "value" => [
771                 %{"tuple" => [":enabled", false]},
772                 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
773                 %{"tuple" => [":seconds_valid", 60]},
774                 %{"tuple" => [":path", ""]},
775                 %{"tuple" => [":key1", nil]},
776                 %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
777                 %{"tuple" => [":regex1", "~r/https:\/\/example.com/"]},
778                 %{"tuple" => [":regex2", "~r/https:\/\/example.com/u"]},
779                 %{"tuple" => [":regex3", "~r/https:\/\/example.com/i"]},
780                 %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]},
781                 %{"tuple" => [":name", "Pleroma"]}
782               ]
783             }
784           ]
785         })
786
787       assert Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma"
788
789       assert json_response_and_validate_schema(conn, 200) == %{
790                "configs" => [
791                  %{
792                    "group" => ":pleroma",
793                    "key" => "Pleroma.Captcha.NotReal",
794                    "value" => [
795                      %{"tuple" => [":enabled", false]},
796                      %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
797                      %{"tuple" => [":seconds_valid", 60]},
798                      %{"tuple" => [":path", ""]},
799                      %{"tuple" => [":key1", nil]},
800                      %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
801                      %{"tuple" => [":regex1", "~r/https:\\/\\/example.com/"]},
802                      %{"tuple" => [":regex2", "~r/https:\\/\\/example.com/u"]},
803                      %{"tuple" => [":regex3", "~r/https:\\/\\/example.com/i"]},
804                      %{"tuple" => [":regex4", "~r/https:\\/\\/example.com/s"]},
805                      %{"tuple" => [":name", "Pleroma"]}
806                    ],
807                    "db" => [
808                      ":enabled",
809                      ":method",
810                      ":seconds_valid",
811                      ":path",
812                      ":key1",
813                      ":partial_chain",
814                      ":regex1",
815                      ":regex2",
816                      ":regex3",
817                      ":regex4",
818                      ":name"
819                    ]
820                  }
821                ],
822                "need_reboot" => false
823              }
824     end
825
826     test "tuples with more than two values", %{conn: conn} do
827       conn =
828         conn
829         |> put_req_header("content-type", "application/json")
830         |> post("/api/pleroma/admin/config", %{
831           configs: [
832             %{
833               "group" => ":pleroma",
834               "key" => "Pleroma.Web.Endpoint.NotReal",
835               "value" => [
836                 %{
837                   "tuple" => [
838                     ":http",
839                     [
840                       %{
841                         "tuple" => [
842                           ":key2",
843                           [
844                             %{
845                               "tuple" => [
846                                 ":_",
847                                 [
848                                   %{
849                                     "tuple" => [
850                                       "/api/v1/streaming",
851                                       "Pleroma.Web.MastodonAPI.WebsocketHandler",
852                                       []
853                                     ]
854                                   },
855                                   %{
856                                     "tuple" => [
857                                       "/websocket",
858                                       "Phoenix.Endpoint.CowboyWebSocket",
859                                       %{
860                                         "tuple" => [
861                                           "Phoenix.Transports.WebSocket",
862                                           %{
863                                             "tuple" => [
864                                               "Pleroma.Web.Endpoint",
865                                               "Pleroma.Web.UserSocket",
866                                               []
867                                             ]
868                                           }
869                                         ]
870                                       }
871                                     ]
872                                   },
873                                   %{
874                                     "tuple" => [
875                                       ":_",
876                                       "Plug.Cowboy.Handler",
877                                       %{"tuple" => ["Pleroma.Web.Endpoint", []]}
878                                     ]
879                                   }
880                                 ]
881                               ]
882                             }
883                           ]
884                         ]
885                       }
886                     ]
887                   ]
888                 }
889               ]
890             }
891           ]
892         })
893
894       assert json_response_and_validate_schema(conn, 200) == %{
895                "configs" => [
896                  %{
897                    "group" => ":pleroma",
898                    "key" => "Pleroma.Web.Endpoint.NotReal",
899                    "value" => [
900                      %{
901                        "tuple" => [
902                          ":http",
903                          [
904                            %{
905                              "tuple" => [
906                                ":key2",
907                                [
908                                  %{
909                                    "tuple" => [
910                                      ":_",
911                                      [
912                                        %{
913                                          "tuple" => [
914                                            "/api/v1/streaming",
915                                            "Pleroma.Web.MastodonAPI.WebsocketHandler",
916                                            []
917                                          ]
918                                        },
919                                        %{
920                                          "tuple" => [
921                                            "/websocket",
922                                            "Phoenix.Endpoint.CowboyWebSocket",
923                                            %{
924                                              "tuple" => [
925                                                "Phoenix.Transports.WebSocket",
926                                                %{
927                                                  "tuple" => [
928                                                    "Pleroma.Web.Endpoint",
929                                                    "Pleroma.Web.UserSocket",
930                                                    []
931                                                  ]
932                                                }
933                                              ]
934                                            }
935                                          ]
936                                        },
937                                        %{
938                                          "tuple" => [
939                                            ":_",
940                                            "Plug.Cowboy.Handler",
941                                            %{"tuple" => ["Pleroma.Web.Endpoint", []]}
942                                          ]
943                                        }
944                                      ]
945                                    ]
946                                  }
947                                ]
948                              ]
949                            }
950                          ]
951                        ]
952                      }
953                    ],
954                    "db" => [":http"]
955                  }
956                ],
957                "need_reboot" => false
958              }
959     end
960
961     test "settings with nesting map", %{conn: conn} do
962       conn =
963         conn
964         |> put_req_header("content-type", "application/json")
965         |> post("/api/pleroma/admin/config", %{
966           configs: [
967             %{
968               "group" => ":pleroma",
969               "key" => ":key1",
970               "value" => [
971                 %{"tuple" => [":key2", "some_val"]},
972                 %{
973                   "tuple" => [
974                     ":key3",
975                     %{
976                       ":max_options" => 20,
977                       ":max_option_chars" => 200,
978                       ":min_expiration" => 0,
979                       ":max_expiration" => 31_536_000,
980                       "nested" => %{
981                         ":max_options" => 20,
982                         ":max_option_chars" => 200,
983                         ":min_expiration" => 0,
984                         ":max_expiration" => 31_536_000
985                       }
986                     }
987                   ]
988                 }
989               ]
990             }
991           ]
992         })
993
994       assert json_response_and_validate_schema(conn, 200) ==
995                %{
996                  "configs" => [
997                    %{
998                      "group" => ":pleroma",
999                      "key" => ":key1",
1000                      "value" => [
1001                        %{"tuple" => [":key2", "some_val"]},
1002                        %{
1003                          "tuple" => [
1004                            ":key3",
1005                            %{
1006                              ":max_expiration" => 31_536_000,
1007                              ":max_option_chars" => 200,
1008                              ":max_options" => 20,
1009                              ":min_expiration" => 0,
1010                              "nested" => %{
1011                                ":max_expiration" => 31_536_000,
1012                                ":max_option_chars" => 200,
1013                                ":max_options" => 20,
1014                                ":min_expiration" => 0
1015                              }
1016                            }
1017                          ]
1018                        }
1019                      ],
1020                      "db" => [":key2", ":key3"]
1021                    }
1022                  ],
1023                  "need_reboot" => false
1024                }
1025     end
1026
1027     test "value as map", %{conn: conn} do
1028       conn =
1029         conn
1030         |> put_req_header("content-type", "application/json")
1031         |> post("/api/pleroma/admin/config", %{
1032           configs: [
1033             %{
1034               "group" => ":pleroma",
1035               "key" => ":key1",
1036               "value" => %{"key" => "some_val"}
1037             }
1038           ]
1039         })
1040
1041       assert json_response_and_validate_schema(conn, 200) ==
1042                %{
1043                  "configs" => [
1044                    %{
1045                      "group" => ":pleroma",
1046                      "key" => ":key1",
1047                      "value" => %{"key" => "some_val"},
1048                      "db" => [":key1"]
1049                    }
1050                  ],
1051                  "need_reboot" => false
1052                }
1053     end
1054
1055     test "queues key as atom", %{conn: conn} do
1056       conn =
1057         conn
1058         |> put_req_header("content-type", "application/json")
1059         |> post("/api/pleroma/admin/config", %{
1060           configs: [
1061             %{
1062               "group" => ":oban",
1063               "key" => ":queues",
1064               "value" => [
1065                 %{"tuple" => [":federator_incoming", 50]},
1066                 %{"tuple" => [":federator_outgoing", 50]},
1067                 %{"tuple" => [":web_push", 50]},
1068                 %{"tuple" => [":mailer", 10]},
1069                 %{"tuple" => [":transmogrifier", 20]},
1070                 %{"tuple" => [":scheduled_activities", 10]},
1071                 %{"tuple" => [":background", 5]}
1072               ]
1073             }
1074           ]
1075         })
1076
1077       assert json_response_and_validate_schema(conn, 200) == %{
1078                "configs" => [
1079                  %{
1080                    "group" => ":oban",
1081                    "key" => ":queues",
1082                    "value" => [
1083                      %{"tuple" => [":federator_incoming", 50]},
1084                      %{"tuple" => [":federator_outgoing", 50]},
1085                      %{"tuple" => [":web_push", 50]},
1086                      %{"tuple" => [":mailer", 10]},
1087                      %{"tuple" => [":transmogrifier", 20]},
1088                      %{"tuple" => [":scheduled_activities", 10]},
1089                      %{"tuple" => [":background", 5]}
1090                    ],
1091                    "db" => [
1092                      ":federator_incoming",
1093                      ":federator_outgoing",
1094                      ":web_push",
1095                      ":mailer",
1096                      ":transmogrifier",
1097                      ":scheduled_activities",
1098                      ":background"
1099                    ]
1100                  }
1101                ],
1102                "need_reboot" => false
1103              }
1104     end
1105
1106     test "delete part of settings by atom subkeys", %{conn: conn} do
1107       insert(:config,
1108         key: :keyaa1,
1109         value: [subkey1: "val1", subkey2: "val2", subkey3: "val3"]
1110       )
1111
1112       conn =
1113         conn
1114         |> put_req_header("content-type", "application/json")
1115         |> post("/api/pleroma/admin/config", %{
1116           configs: [
1117             %{
1118               group: ":pleroma",
1119               key: ":keyaa1",
1120               subkeys: [":subkey1", ":subkey3"],
1121               delete: true
1122             }
1123           ]
1124         })
1125
1126       assert json_response_and_validate_schema(conn, 200) == %{
1127                "configs" => [
1128                  %{
1129                    "group" => ":pleroma",
1130                    "key" => ":keyaa1",
1131                    "value" => [%{"tuple" => [":subkey2", "val2"]}],
1132                    "db" => [":subkey2"]
1133                  }
1134                ],
1135                "need_reboot" => false
1136              }
1137     end
1138
1139     test "proxy tuple localhost", %{conn: conn} do
1140       conn =
1141         conn
1142         |> put_req_header("content-type", "application/json")
1143         |> post("/api/pleroma/admin/config", %{
1144           configs: [
1145             %{
1146               group: ":pleroma",
1147               key: ":http",
1148               value: [
1149                 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]}
1150               ]
1151             }
1152           ]
1153         })
1154
1155       assert %{
1156                "configs" => [
1157                  %{
1158                    "group" => ":pleroma",
1159                    "key" => ":http",
1160                    "value" => value,
1161                    "db" => db
1162                  }
1163                ]
1164              } = json_response_and_validate_schema(conn, 200)
1165
1166       assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]} in value
1167       assert ":proxy_url" in db
1168     end
1169
1170     test "proxy tuple domain", %{conn: conn} do
1171       conn =
1172         conn
1173         |> put_req_header("content-type", "application/json")
1174         |> post("/api/pleroma/admin/config", %{
1175           configs: [
1176             %{
1177               group: ":pleroma",
1178               key: ":http",
1179               value: [
1180                 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]}
1181               ]
1182             }
1183           ]
1184         })
1185
1186       assert %{
1187                "configs" => [
1188                  %{
1189                    "group" => ":pleroma",
1190                    "key" => ":http",
1191                    "value" => value,
1192                    "db" => db
1193                  }
1194                ]
1195              } = json_response_and_validate_schema(conn, 200)
1196
1197       assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]} in value
1198       assert ":proxy_url" in db
1199     end
1200
1201     test "proxy tuple ip", %{conn: conn} do
1202       conn =
1203         conn
1204         |> put_req_header("content-type", "application/json")
1205         |> post("/api/pleroma/admin/config", %{
1206           configs: [
1207             %{
1208               group: ":pleroma",
1209               key: ":http",
1210               value: [
1211                 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]}
1212               ]
1213             }
1214           ]
1215         })
1216
1217       assert %{
1218                "configs" => [
1219                  %{
1220                    "group" => ":pleroma",
1221                    "key" => ":http",
1222                    "value" => value,
1223                    "db" => db
1224                  }
1225                ]
1226              } = json_response_and_validate_schema(conn, 200)
1227
1228       assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]} in value
1229       assert ":proxy_url" in db
1230     end
1231
1232     @tag capture_log: true
1233     test "doesn't set keys not in the whitelist", %{conn: conn} do
1234       clear_config(:database_config_whitelist, [
1235         {:pleroma, :key1},
1236         {:pleroma, :key2},
1237         {:pleroma, Pleroma.Captcha.NotReal},
1238         {:not_real}
1239       ])
1240
1241       conn
1242       |> put_req_header("content-type", "application/json")
1243       |> post("/api/pleroma/admin/config", %{
1244         configs: [
1245           %{group: ":pleroma", key: ":key1", value: "value1"},
1246           %{group: ":pleroma", key: ":key2", value: "value2"},
1247           %{group: ":pleroma", key: ":key3", value: "value3"},
1248           %{group: ":pleroma", key: "Pleroma.Web.Endpoint.NotReal", value: "value4"},
1249           %{group: ":pleroma", key: "Pleroma.Captcha.NotReal", value: "value5"},
1250           %{group: ":not_real", key: ":anything", value: "value6"}
1251         ]
1252       })
1253
1254       assert Application.get_env(:pleroma, :key1) == "value1"
1255       assert Application.get_env(:pleroma, :key2) == "value2"
1256       assert Application.get_env(:pleroma, :key3) == nil
1257       assert Application.get_env(:pleroma, Pleroma.Web.Endpoint.NotReal) == nil
1258       assert Application.get_env(:pleroma, Pleroma.Captcha.NotReal) == "value5"
1259       assert Application.get_env(:not_real, :anything) == "value6"
1260     end
1261
1262     test "args for Pleroma.Upload.Filter.Mogrify with custom tuples", %{conn: conn} do
1263       clear_config(Pleroma.Upload.Filter.Mogrify)
1264
1265       assert conn
1266              |> put_req_header("content-type", "application/json")
1267              |> post("/api/pleroma/admin/config", %{
1268                configs: [
1269                  %{
1270                    group: ":pleroma",
1271                    key: "Pleroma.Upload.Filter.Mogrify",
1272                    value: [
1273                      %{"tuple" => [":args", ["auto-orient", "strip"]]}
1274                    ]
1275                  }
1276                ]
1277              })
1278              |> json_response_and_validate_schema(200) == %{
1279                "configs" => [
1280                  %{
1281                    "group" => ":pleroma",
1282                    "key" => "Pleroma.Upload.Filter.Mogrify",
1283                    "value" => [
1284                      %{"tuple" => [":args", ["auto-orient", "strip"]]}
1285                    ],
1286                    "db" => [":args"]
1287                  }
1288                ],
1289                "need_reboot" => false
1290              }
1291
1292       assert Config.get(Pleroma.Upload.Filter.Mogrify) == [args: ["auto-orient", "strip"]]
1293
1294       assert conn
1295              |> put_req_header("content-type", "application/json")
1296              |> post("/api/pleroma/admin/config", %{
1297                configs: [
1298                  %{
1299                    group: ":pleroma",
1300                    key: "Pleroma.Upload.Filter.Mogrify",
1301                    value: [
1302                      %{
1303                        "tuple" => [
1304                          ":args",
1305                          [
1306                            "auto-orient",
1307                            "strip",
1308                            "{\"implode\", \"1\"}",
1309                            "{\"resize\", \"3840x1080>\"}"
1310                          ]
1311                        ]
1312                      }
1313                    ]
1314                  }
1315                ]
1316              })
1317              |> json_response(200) == %{
1318                "configs" => [
1319                  %{
1320                    "group" => ":pleroma",
1321                    "key" => "Pleroma.Upload.Filter.Mogrify",
1322                    "value" => [
1323                      %{
1324                        "tuple" => [
1325                          ":args",
1326                          [
1327                            "auto-orient",
1328                            "strip",
1329                            "{\"implode\", \"1\"}",
1330                            "{\"resize\", \"3840x1080>\"}"
1331                          ]
1332                        ]
1333                      }
1334                    ],
1335                    "db" => [":args"]
1336                  }
1337                ],
1338                "need_reboot" => false
1339              }
1340
1341       assert Config.get(Pleroma.Upload.Filter.Mogrify) == [
1342                args: ["auto-orient", "strip", {"implode", "1"}, {"resize", "3840x1080>"}]
1343              ]
1344     end
1345
1346     test "enables the welcome messages", %{conn: conn} do
1347       clear_config([:welcome])
1348
1349       params = %{
1350         "group" => ":pleroma",
1351         "key" => ":welcome",
1352         "value" => [
1353           %{
1354             "tuple" => [
1355               ":direct_message",
1356               [
1357                 %{"tuple" => [":enabled", true]},
1358                 %{"tuple" => [":message", "Welcome to Pleroma!"]},
1359                 %{"tuple" => [":sender_nickname", "pleroma"]}
1360               ]
1361             ]
1362           },
1363           %{
1364             "tuple" => [
1365               ":chat_message",
1366               [
1367                 %{"tuple" => [":enabled", true]},
1368                 %{"tuple" => [":message", "Welcome to Pleroma!"]},
1369                 %{"tuple" => [":sender_nickname", "pleroma"]}
1370               ]
1371             ]
1372           },
1373           %{
1374             "tuple" => [
1375               ":email",
1376               [
1377                 %{"tuple" => [":enabled", true]},
1378                 %{"tuple" => [":sender", %{"tuple" => ["pleroma@dev.dev", "Pleroma"]}]},
1379                 %{"tuple" => [":subject", "Welcome to <%= instance_name %>!"]},
1380                 %{"tuple" => [":html", "Welcome to <%= instance_name %>!"]},
1381                 %{"tuple" => [":text", "Welcome to <%= instance_name %>!"]}
1382               ]
1383             ]
1384           }
1385         ]
1386       }
1387
1388       refute Pleroma.User.WelcomeEmail.enabled?()
1389       refute Pleroma.User.WelcomeMessage.enabled?()
1390       refute Pleroma.User.WelcomeChatMessage.enabled?()
1391
1392       res =
1393         assert conn
1394                |> put_req_header("content-type", "application/json")
1395                |> post("/api/pleroma/admin/config", %{"configs" => [params]})
1396                |> json_response_and_validate_schema(200)
1397
1398       assert Pleroma.User.WelcomeEmail.enabled?()
1399       assert Pleroma.User.WelcomeMessage.enabled?()
1400       assert Pleroma.User.WelcomeChatMessage.enabled?()
1401
1402       assert res == %{
1403                "configs" => [
1404                  %{
1405                    "db" => [":direct_message", ":chat_message", ":email"],
1406                    "group" => ":pleroma",
1407                    "key" => ":welcome",
1408                    "value" => params["value"]
1409                  }
1410                ],
1411                "need_reboot" => false
1412              }
1413     end
1414
1415     test "custom instance thumbnail", %{conn: conn} do
1416       clear_config([:instance])
1417
1418       params = %{
1419         "group" => ":pleroma",
1420         "key" => ":instance",
1421         "value" => [
1422           %{
1423             "tuple" => [
1424               ":instance_thumbnail",
1425               "https://example.com/media/new_thumbnail.jpg"
1426             ]
1427           }
1428         ]
1429       }
1430
1431       assert conn
1432              |> put_req_header("content-type", "application/json")
1433              |> post("/api/pleroma/admin/config", %{"configs" => [params]})
1434              |> json_response_and_validate_schema(200) ==
1435                %{
1436                  "configs" => [
1437                    %{
1438                      "db" => [":instance_thumbnail"],
1439                      "group" => ":pleroma",
1440                      "key" => ":instance",
1441                      "value" => params["value"]
1442                    }
1443                  ],
1444                  "need_reboot" => false
1445                }
1446
1447       assert conn
1448              |> get("/api/v1/instance")
1449              |> json_response_and_validate_schema(200)
1450              |> Map.take(["thumbnail"]) ==
1451                %{"thumbnail" => "https://example.com/media/new_thumbnail.jpg"}
1452     end
1453
1454     test "Concurrent Limiter", %{conn: conn} do
1455       clear_config([ConcurrentLimiter])
1456
1457       params = %{
1458         "group" => ":pleroma",
1459         "key" => "ConcurrentLimiter",
1460         "value" => [
1461           %{
1462             "tuple" => [
1463               "Pleroma.Web.RichMedia.Helpers",
1464               [
1465                 %{"tuple" => [":max_running", 6]},
1466                 %{"tuple" => [":max_waiting", 6]}
1467               ]
1468             ]
1469           },
1470           %{
1471             "tuple" => [
1472               "Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy",
1473               [
1474                 %{"tuple" => [":max_running", 7]},
1475                 %{"tuple" => [":max_waiting", 7]}
1476               ]
1477             ]
1478           }
1479         ]
1480       }
1481
1482       assert conn
1483              |> put_req_header("content-type", "application/json")
1484              |> post("/api/pleroma/admin/config", %{"configs" => [params]})
1485              |> json_response_and_validate_schema(200)
1486     end
1487   end
1488
1489   describe "GET /api/pleroma/admin/config/descriptions" do
1490     test "structure", %{conn: conn} do
1491       conn = get(conn, "/api/pleroma/admin/config/descriptions")
1492
1493       assert [child | _others] = json_response_and_validate_schema(conn, 200)
1494
1495       assert child["children"]
1496       assert child["key"]
1497       assert String.starts_with?(child["group"], ":")
1498       assert child["description"]
1499     end
1500
1501     test "filters by database configuration whitelist", %{conn: conn} do
1502       clear_config(:database_config_whitelist, [
1503         {:pleroma, :instance},
1504         {:pleroma, :activitypub},
1505         {:pleroma, Pleroma.Upload}
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) == 3
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     end
1525   end
1526 end