admin_api_controller.ex (32782B)
1 # Pleroma: A lightweight social networking server 2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> 3 # SPDX-License-Identifier: AGPL-3.0-only 4 5 defmodule Pleroma.Web.AdminAPI.AdminAPIController do 6 use Pleroma.Web, :controller 7 8 import Pleroma.Web.ControllerHelper, only: [json_response: 3] 9 10 alias Pleroma.Activity 11 alias Pleroma.Config 12 alias Pleroma.ConfigDB 13 alias Pleroma.MFA 14 alias Pleroma.ModerationLog 15 alias Pleroma.Plugs.OAuthScopesPlug 16 alias Pleroma.ReportNote 17 alias Pleroma.Stats 18 alias Pleroma.User 19 alias Pleroma.UserInviteToken 20 alias Pleroma.Web.ActivityPub.ActivityPub 21 alias Pleroma.Web.ActivityPub.Builder 22 alias Pleroma.Web.ActivityPub.Pipeline 23 alias Pleroma.Web.ActivityPub.Relay 24 alias Pleroma.Web.ActivityPub.Utils 25 alias Pleroma.Web.AdminAPI 26 alias Pleroma.Web.AdminAPI.AccountView 27 alias Pleroma.Web.AdminAPI.ConfigView 28 alias Pleroma.Web.AdminAPI.ModerationLogView 29 alias Pleroma.Web.AdminAPI.Report 30 alias Pleroma.Web.AdminAPI.ReportView 31 alias Pleroma.Web.AdminAPI.Search 32 alias Pleroma.Web.CommonAPI 33 alias Pleroma.Web.Endpoint 34 alias Pleroma.Web.MastodonAPI 35 alias Pleroma.Web.MastodonAPI.AppView 36 alias Pleroma.Web.OAuth.App 37 alias Pleroma.Web.Router 38 39 require Logger 40 41 @descriptions Pleroma.Docs.JSON.compile() 42 @users_page_size 50 43 44 plug( 45 OAuthScopesPlug, 46 %{scopes: ["read:accounts"], admin: true} 47 when action in [:list_users, :user_show, :right_get, :show_user_credentials] 48 ) 49 50 plug( 51 OAuthScopesPlug, 52 %{scopes: ["write:accounts"], admin: true} 53 when action in [ 54 :get_password_reset, 55 :force_password_reset, 56 :user_delete, 57 :users_create, 58 :user_toggle_activation, 59 :user_activate, 60 :user_deactivate, 61 :tag_users, 62 :untag_users, 63 :right_add, 64 :right_add_multiple, 65 :right_delete, 66 :disable_mfa, 67 :right_delete_multiple, 68 :update_user_credentials 69 ] 70 ) 71 72 plug(OAuthScopesPlug, %{scopes: ["read:invites"], admin: true} when action == :invites) 73 74 plug( 75 OAuthScopesPlug, 76 %{scopes: ["write:invites"], admin: true} 77 when action in [:create_invite_token, :revoke_invite, :email_invite] 78 ) 79 80 plug( 81 OAuthScopesPlug, 82 %{scopes: ["write:follows"], admin: true} 83 when action in [:user_follow, :user_unfollow, :relay_follow, :relay_unfollow] 84 ) 85 86 plug( 87 OAuthScopesPlug, 88 %{scopes: ["read:reports"], admin: true} 89 when action in [:list_reports, :report_show] 90 ) 91 92 plug( 93 OAuthScopesPlug, 94 %{scopes: ["write:reports"], admin: true} 95 when action in [:reports_update, :report_notes_create, :report_notes_delete] 96 ) 97 98 plug( 99 OAuthScopesPlug, 100 %{scopes: ["read:statuses"], admin: true} 101 when action in [:list_statuses, :list_user_statuses, :list_instance_statuses, :status_show] 102 ) 103 104 plug( 105 OAuthScopesPlug, 106 %{scopes: ["write:statuses"], admin: true} 107 when action in [:status_update, :status_delete] 108 ) 109 110 plug( 111 OAuthScopesPlug, 112 %{scopes: ["read"], admin: true} 113 when action in [ 114 :config_show, 115 :list_log, 116 :stats, 117 :relay_list, 118 :config_descriptions, 119 :need_reboot 120 ] 121 ) 122 123 plug( 124 OAuthScopesPlug, 125 %{scopes: ["write"], admin: true} 126 when action in [ 127 :restart, 128 :config_update, 129 :resend_confirmation_email, 130 :confirm_email, 131 :oauth_app_create, 132 :oauth_app_list, 133 :oauth_app_update, 134 :oauth_app_delete, 135 :reload_emoji 136 ] 137 ) 138 139 action_fallback(:errors) 140 141 def user_delete(conn, %{"nickname" => nickname}) do 142 user_delete(conn, %{"nicknames" => [nickname]}) 143 end 144 145 def user_delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do 146 users = 147 nicknames 148 |> Enum.map(&User.get_cached_by_nickname/1) 149 150 users 151 |> Enum.each(fn user -> 152 {:ok, delete_data, _} = Builder.delete(admin, user.ap_id) 153 Pipeline.common_pipeline(delete_data, local: true) 154 end) 155 156 ModerationLog.insert_log(%{ 157 actor: admin, 158 subject: users, 159 action: "delete" 160 }) 161 162 conn 163 |> json(nicknames) 164 end 165 166 def user_follow(%{assigns: %{user: admin}} = conn, %{ 167 "follower" => follower_nick, 168 "followed" => followed_nick 169 }) do 170 with %User{} = follower <- User.get_cached_by_nickname(follower_nick), 171 %User{} = followed <- User.get_cached_by_nickname(followed_nick) do 172 User.follow(follower, followed) 173 174 ModerationLog.insert_log(%{ 175 actor: admin, 176 followed: followed, 177 follower: follower, 178 action: "follow" 179 }) 180 end 181 182 conn 183 |> json("ok") 184 end 185 186 def user_unfollow(%{assigns: %{user: admin}} = conn, %{ 187 "follower" => follower_nick, 188 "followed" => followed_nick 189 }) do 190 with %User{} = follower <- User.get_cached_by_nickname(follower_nick), 191 %User{} = followed <- User.get_cached_by_nickname(followed_nick) do 192 User.unfollow(follower, followed) 193 194 ModerationLog.insert_log(%{ 195 actor: admin, 196 followed: followed, 197 follower: follower, 198 action: "unfollow" 199 }) 200 end 201 202 conn 203 |> json("ok") 204 end 205 206 def users_create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do 207 changesets = 208 Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} -> 209 user_data = %{ 210 nickname: nickname, 211 name: nickname, 212 email: email, 213 password: password, 214 password_confirmation: password, 215 bio: "." 216 } 217 218 User.register_changeset(%User{}, user_data, need_confirmation: false) 219 end) 220 |> Enum.reduce(Ecto.Multi.new(), fn changeset, multi -> 221 Ecto.Multi.insert(multi, Ecto.UUID.generate(), changeset) 222 end) 223 224 case Pleroma.Repo.transaction(changesets) do 225 {:ok, users} -> 226 res = 227 users 228 |> Map.values() 229 |> Enum.map(fn user -> 230 {:ok, user} = User.post_register_action(user) 231 232 user 233 end) 234 |> Enum.map(&AccountView.render("created.json", %{user: &1})) 235 236 ModerationLog.insert_log(%{ 237 actor: admin, 238 subjects: Map.values(users), 239 action: "create" 240 }) 241 242 conn 243 |> json(res) 244 245 {:error, id, changeset, _} -> 246 res = 247 Enum.map(changesets.operations, fn 248 {current_id, {:changeset, _current_changeset, _}} when current_id == id -> 249 AccountView.render("create-error.json", %{changeset: changeset}) 250 251 {_, {:changeset, current_changeset, _}} -> 252 AccountView.render("create-error.json", %{changeset: current_changeset}) 253 end) 254 255 conn 256 |> put_status(:conflict) 257 |> json(res) 258 end 259 end 260 261 def user_show(conn, %{"nickname" => nickname}) do 262 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do 263 conn 264 |> put_view(AccountView) 265 |> render("show.json", %{user: user}) 266 else 267 _ -> {:error, :not_found} 268 end 269 end 270 271 def list_instance_statuses(conn, %{"instance" => instance} = params) do 272 with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true 273 {page, page_size} = page_params(params) 274 275 activities = 276 ActivityPub.fetch_statuses(nil, %{ 277 "instance" => instance, 278 "limit" => page_size, 279 "offset" => (page - 1) * page_size, 280 "exclude_reblogs" => !with_reblogs && "true" 281 }) 282 283 conn 284 |> put_view(AdminAPI.StatusView) 285 |> render("index.json", %{activities: activities, as: :activity}) 286 end 287 288 def list_user_statuses(conn, %{"nickname" => nickname} = params) do 289 with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true 290 godmode = params["godmode"] == "true" || params["godmode"] == true 291 292 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do 293 {_, page_size} = page_params(params) 294 295 activities = 296 ActivityPub.fetch_user_activities(user, nil, %{ 297 "limit" => page_size, 298 "godmode" => godmode, 299 "exclude_reblogs" => !with_reblogs && "true" 300 }) 301 302 conn 303 |> put_view(MastodonAPI.StatusView) 304 |> render("index.json", %{activities: activities, as: :activity}) 305 else 306 _ -> {:error, :not_found} 307 end 308 end 309 310 def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do 311 user = User.get_cached_by_nickname(nickname) 312 313 {:ok, updated_user} = User.deactivate(user, !user.deactivated) 314 315 action = if user.deactivated, do: "activate", else: "deactivate" 316 317 ModerationLog.insert_log(%{ 318 actor: admin, 319 subject: [user], 320 action: action 321 }) 322 323 conn 324 |> put_view(AccountView) 325 |> render("show.json", %{user: updated_user}) 326 end 327 328 def user_activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do 329 users = Enum.map(nicknames, &User.get_cached_by_nickname/1) 330 {:ok, updated_users} = User.deactivate(users, false) 331 332 ModerationLog.insert_log(%{ 333 actor: admin, 334 subject: users, 335 action: "activate" 336 }) 337 338 conn 339 |> put_view(AccountView) 340 |> render("index.json", %{users: Keyword.values(updated_users)}) 341 end 342 343 def user_deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do 344 users = Enum.map(nicknames, &User.get_cached_by_nickname/1) 345 {:ok, updated_users} = User.deactivate(users, true) 346 347 ModerationLog.insert_log(%{ 348 actor: admin, 349 subject: users, 350 action: "deactivate" 351 }) 352 353 conn 354 |> put_view(AccountView) 355 |> render("index.json", %{users: Keyword.values(updated_users)}) 356 end 357 358 def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do 359 with {:ok, _} <- User.tag(nicknames, tags) do 360 ModerationLog.insert_log(%{ 361 actor: admin, 362 nicknames: nicknames, 363 tags: tags, 364 action: "tag" 365 }) 366 367 json_response(conn, :no_content, "") 368 end 369 end 370 371 def untag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do 372 with {:ok, _} <- User.untag(nicknames, tags) do 373 ModerationLog.insert_log(%{ 374 actor: admin, 375 nicknames: nicknames, 376 tags: tags, 377 action: "untag" 378 }) 379 380 json_response(conn, :no_content, "") 381 end 382 end 383 384 def list_users(conn, params) do 385 {page, page_size} = page_params(params) 386 filters = maybe_parse_filters(params["filters"]) 387 388 search_params = %{ 389 query: params["query"], 390 page: page, 391 page_size: page_size, 392 tags: params["tags"], 393 name: params["name"], 394 email: params["email"] 395 } 396 397 with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)) do 398 json( 399 conn, 400 AccountView.render("index.json", users: users, count: count, page_size: page_size) 401 ) 402 end 403 end 404 405 @filters ~w(local external active deactivated is_admin is_moderator) 406 407 @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{} 408 defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{} 409 410 defp maybe_parse_filters(filters) do 411 filters 412 |> String.split(",") 413 |> Enum.filter(&Enum.member?(@filters, &1)) 414 |> Enum.map(&String.to_atom(&1)) 415 |> Enum.into(%{}, &{&1, true}) 416 end 417 418 def right_add_multiple(%{assigns: %{user: admin}} = conn, %{ 419 "permission_group" => permission_group, 420 "nicknames" => nicknames 421 }) 422 when permission_group in ["moderator", "admin"] do 423 update = %{:"is_#{permission_group}" => true} 424 425 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1) 426 427 for u <- users, do: User.admin_api_update(u, update) 428 429 ModerationLog.insert_log(%{ 430 action: "grant", 431 actor: admin, 432 subject: users, 433 permission: permission_group 434 }) 435 436 json(conn, update) 437 end 438 439 def right_add_multiple(conn, _) do 440 render_error(conn, :not_found, "No such permission_group") 441 end 442 443 def right_add(%{assigns: %{user: admin}} = conn, %{ 444 "permission_group" => permission_group, 445 "nickname" => nickname 446 }) 447 when permission_group in ["moderator", "admin"] do 448 fields = %{:"is_#{permission_group}" => true} 449 450 {:ok, user} = 451 nickname 452 |> User.get_cached_by_nickname() 453 |> User.admin_api_update(fields) 454 455 ModerationLog.insert_log(%{ 456 action: "grant", 457 actor: admin, 458 subject: [user], 459 permission: permission_group 460 }) 461 462 json(conn, fields) 463 end 464 465 def right_add(conn, _) do 466 render_error(conn, :not_found, "No such permission_group") 467 end 468 469 def right_get(conn, %{"nickname" => nickname}) do 470 user = User.get_cached_by_nickname(nickname) 471 472 conn 473 |> json(%{ 474 is_moderator: user.is_moderator, 475 is_admin: user.is_admin 476 }) 477 end 478 479 def right_delete_multiple( 480 %{assigns: %{user: %{nickname: admin_nickname} = admin}} = conn, 481 %{ 482 "permission_group" => permission_group, 483 "nicknames" => nicknames 484 } 485 ) 486 when permission_group in ["moderator", "admin"] do 487 with false <- Enum.member?(nicknames, admin_nickname) do 488 update = %{:"is_#{permission_group}" => false} 489 490 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1) 491 492 for u <- users, do: User.admin_api_update(u, update) 493 494 ModerationLog.insert_log(%{ 495 action: "revoke", 496 actor: admin, 497 subject: users, 498 permission: permission_group 499 }) 500 501 json(conn, update) 502 else 503 _ -> render_error(conn, :forbidden, "You can't revoke your own admin/moderator status.") 504 end 505 end 506 507 def right_delete_multiple(conn, _) do 508 render_error(conn, :not_found, "No such permission_group") 509 end 510 511 def right_delete( 512 %{assigns: %{user: admin}} = conn, 513 %{ 514 "permission_group" => permission_group, 515 "nickname" => nickname 516 } 517 ) 518 when permission_group in ["moderator", "admin"] do 519 fields = %{:"is_#{permission_group}" => false} 520 521 {:ok, user} = 522 nickname 523 |> User.get_cached_by_nickname() 524 |> User.admin_api_update(fields) 525 526 ModerationLog.insert_log(%{ 527 action: "revoke", 528 actor: admin, 529 subject: [user], 530 permission: permission_group 531 }) 532 533 json(conn, fields) 534 end 535 536 def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname" => nickname}) do 537 render_error(conn, :forbidden, "You can't revoke your own admin status.") 538 end 539 540 def relay_list(conn, _params) do 541 with {:ok, list} <- Relay.list() do 542 json(conn, %{relays: list}) 543 else 544 _ -> 545 conn 546 |> put_status(500) 547 end 548 end 549 550 def relay_follow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do 551 with {:ok, _message} <- Relay.follow(target) do 552 ModerationLog.insert_log(%{ 553 action: "relay_follow", 554 actor: admin, 555 target: target 556 }) 557 558 json(conn, target) 559 else 560 _ -> 561 conn 562 |> put_status(500) 563 |> json(target) 564 end 565 end 566 567 def relay_unfollow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do 568 with {:ok, _message} <- Relay.unfollow(target) do 569 ModerationLog.insert_log(%{ 570 action: "relay_unfollow", 571 actor: admin, 572 target: target 573 }) 574 575 json(conn, target) 576 else 577 _ -> 578 conn 579 |> put_status(500) 580 |> json(target) 581 end 582 end 583 584 @doc "Sends registration invite via email" 585 def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do 586 with {_, false} <- {:registrations_open, Config.get([:instance, :registrations_open])}, 587 {_, true} <- {:invites_enabled, Config.get([:instance, :invites_enabled])}, 588 {:ok, invite_token} <- UserInviteToken.create_invite(), 589 email <- 590 Pleroma.Emails.UserEmail.user_invitation_email( 591 user, 592 invite_token, 593 email, 594 params["name"] 595 ), 596 {:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do 597 json_response(conn, :no_content, "") 598 else 599 {:registrations_open, _} -> 600 errors( 601 conn, 602 {:error, "To send invites you need to set the `registrations_open` option to false."} 603 ) 604 605 {:invites_enabled, _} -> 606 errors( 607 conn, 608 {:error, "To send invites you need to set the `invites_enabled` option to true."} 609 ) 610 end 611 end 612 613 @doc "Create an account registration invite token" 614 def create_invite_token(conn, params) do 615 opts = %{} 616 617 opts = 618 if params["max_use"], 619 do: Map.put(opts, :max_use, params["max_use"]), 620 else: opts 621 622 opts = 623 if params["expires_at"], 624 do: Map.put(opts, :expires_at, params["expires_at"]), 625 else: opts 626 627 {:ok, invite} = UserInviteToken.create_invite(opts) 628 629 json(conn, AccountView.render("invite.json", %{invite: invite})) 630 end 631 632 @doc "Get list of created invites" 633 def invites(conn, _params) do 634 invites = UserInviteToken.list_invites() 635 636 conn 637 |> put_view(AccountView) 638 |> render("invites.json", %{invites: invites}) 639 end 640 641 @doc "Revokes invite by token" 642 def revoke_invite(conn, %{"token" => token}) do 643 with {:ok, invite} <- UserInviteToken.find_by_token(token), 644 {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do 645 conn 646 |> put_view(AccountView) 647 |> render("invite.json", %{invite: updated_invite}) 648 else 649 nil -> {:error, :not_found} 650 end 651 end 652 653 @doc "Get a password reset token (base64 string) for given nickname" 654 def get_password_reset(conn, %{"nickname" => nickname}) do 655 (%User{local: true} = user) = User.get_cached_by_nickname(nickname) 656 {:ok, token} = Pleroma.PasswordResetToken.create_token(user) 657 658 conn 659 |> json(%{ 660 token: token.token, 661 link: Router.Helpers.reset_password_url(Endpoint, :reset, token.token) 662 }) 663 end 664 665 @doc "Force password reset for a given user" 666 def force_password_reset(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do 667 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1) 668 669 Enum.each(users, &User.force_password_reset_async/1) 670 671 ModerationLog.insert_log(%{ 672 actor: admin, 673 subject: users, 674 action: "force_password_reset" 675 }) 676 677 json_response(conn, :no_content, "") 678 end 679 680 @doc "Disable mfa for user's account." 681 def disable_mfa(conn, %{"nickname" => nickname}) do 682 case User.get_by_nickname(nickname) do 683 %User{} = user -> 684 MFA.disable(user) 685 json(conn, nickname) 686 687 _ -> 688 {:error, :not_found} 689 end 690 end 691 692 @doc "Show a given user's credentials" 693 def show_user_credentials(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do 694 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do 695 conn 696 |> put_view(AccountView) 697 |> render("credentials.json", %{user: user, for: admin}) 698 else 699 _ -> {:error, :not_found} 700 end 701 end 702 703 @doc "Updates a given user" 704 def update_user_credentials( 705 %{assigns: %{user: admin}} = conn, 706 %{"nickname" => nickname} = params 707 ) do 708 with {_, user} <- {:user, User.get_cached_by_nickname(nickname)}, 709 {:ok, _user} <- 710 User.update_as_admin(user, params) do 711 ModerationLog.insert_log(%{ 712 actor: admin, 713 subject: [user], 714 action: "updated_users" 715 }) 716 717 if params["password"] do 718 User.force_password_reset_async(user) 719 end 720 721 ModerationLog.insert_log(%{ 722 actor: admin, 723 subject: [user], 724 action: "force_password_reset" 725 }) 726 727 json(conn, %{status: "success"}) 728 else 729 {:error, changeset} -> 730 {_, {error, _}} = Enum.at(changeset.errors, 0) 731 json(conn, %{error: "New password #{error}."}) 732 733 _ -> 734 json(conn, %{error: "Unable to change password."}) 735 end 736 end 737 738 def list_reports(conn, params) do 739 {page, page_size} = page_params(params) 740 741 reports = Utils.get_reports(params, page, page_size) 742 743 conn 744 |> put_view(ReportView) 745 |> render("index.json", %{reports: reports}) 746 end 747 748 def report_show(conn, %{"id" => id}) do 749 with %Activity{} = report <- Activity.get_by_id(id) do 750 conn 751 |> put_view(ReportView) 752 |> render("show.json", Report.extract_report_info(report)) 753 else 754 _ -> {:error, :not_found} 755 end 756 end 757 758 def reports_update(%{assigns: %{user: admin}} = conn, %{"reports" => reports}) do 759 result = 760 reports 761 |> Enum.map(fn report -> 762 with {:ok, activity} <- CommonAPI.update_report_state(report["id"], report["state"]) do 763 ModerationLog.insert_log(%{ 764 action: "report_update", 765 actor: admin, 766 subject: activity 767 }) 768 769 activity 770 else 771 {:error, message} -> %{id: report["id"], error: message} 772 end 773 end) 774 775 case Enum.any?(result, &Map.has_key?(&1, :error)) do 776 true -> json_response(conn, :bad_request, result) 777 false -> json_response(conn, :no_content, "") 778 end 779 end 780 781 def report_notes_create(%{assigns: %{user: user}} = conn, %{ 782 "id" => report_id, 783 "content" => content 784 }) do 785 with {:ok, _} <- ReportNote.create(user.id, report_id, content) do 786 ModerationLog.insert_log(%{ 787 action: "report_note", 788 actor: user, 789 subject: Activity.get_by_id(report_id), 790 text: content 791 }) 792 793 json_response(conn, :no_content, "") 794 else 795 _ -> json_response(conn, :bad_request, "") 796 end 797 end 798 799 def report_notes_delete(%{assigns: %{user: user}} = conn, %{ 800 "id" => note_id, 801 "report_id" => report_id 802 }) do 803 with {:ok, note} <- ReportNote.destroy(note_id) do 804 ModerationLog.insert_log(%{ 805 action: "report_note_delete", 806 actor: user, 807 subject: Activity.get_by_id(report_id), 808 text: note.content 809 }) 810 811 json_response(conn, :no_content, "") 812 else 813 _ -> json_response(conn, :bad_request, "") 814 end 815 end 816 817 def list_statuses(%{assigns: %{user: _admin}} = conn, params) do 818 godmode = params["godmode"] == "true" || params["godmode"] == true 819 local_only = params["local_only"] == "true" || params["local_only"] == true 820 with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true 821 {page, page_size} = page_params(params) 822 823 activities = 824 ActivityPub.fetch_statuses(nil, %{ 825 "godmode" => godmode, 826 "local_only" => local_only, 827 "limit" => page_size, 828 "offset" => (page - 1) * page_size, 829 "exclude_reblogs" => !with_reblogs && "true" 830 }) 831 832 conn 833 |> put_view(AdminAPI.StatusView) 834 |> render("index.json", %{activities: activities, as: :activity}) 835 end 836 837 def status_show(conn, %{"id" => id}) do 838 with %Activity{} = activity <- Activity.get_by_id(id) do 839 conn 840 |> put_view(MastodonAPI.StatusView) 841 |> render("show.json", %{activity: activity}) 842 else 843 _ -> errors(conn, {:error, :not_found}) 844 end 845 end 846 847 def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do 848 params = 849 params 850 |> Map.take(["sensitive", "visibility"]) 851 |> Map.new(fn {key, value} -> {String.to_existing_atom(key), value} end) 852 853 with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do 854 {:ok, sensitive} = Ecto.Type.cast(:boolean, params[:sensitive]) 855 856 ModerationLog.insert_log(%{ 857 action: "status_update", 858 actor: admin, 859 subject: activity, 860 sensitive: sensitive, 861 visibility: params[:visibility] 862 }) 863 864 conn 865 |> put_view(MastodonAPI.StatusView) 866 |> render("show.json", %{activity: activity}) 867 end 868 end 869 870 def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do 871 with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do 872 ModerationLog.insert_log(%{ 873 action: "status_delete", 874 actor: user, 875 subject_id: id 876 }) 877 878 json(conn, %{}) 879 end 880 end 881 882 def list_log(conn, params) do 883 {page, page_size} = page_params(params) 884 885 log = 886 ModerationLog.get_all(%{ 887 page: page, 888 page_size: page_size, 889 start_date: params["start_date"], 890 end_date: params["end_date"], 891 user_id: params["user_id"], 892 search: params["search"] 893 }) 894 895 conn 896 |> put_view(ModerationLogView) 897 |> render("index.json", %{log: log}) 898 end 899 900 def config_descriptions(conn, _params) do 901 descriptions = Enum.filter(@descriptions, &whitelisted_config?/1) 902 903 json(conn, descriptions) 904 end 905 906 def config_show(conn, %{"only_db" => true}) do 907 with :ok <- configurable_from_database(conn) do 908 configs = Pleroma.Repo.all(ConfigDB) 909 910 conn 911 |> put_view(ConfigView) 912 |> render("index.json", %{configs: configs}) 913 end 914 end 915 916 def config_show(conn, _params) do 917 with :ok <- configurable_from_database(conn) do 918 configs = ConfigDB.get_all_as_keyword() 919 920 merged = 921 Config.Holder.default_config() 922 |> ConfigDB.merge(configs) 923 |> Enum.map(fn {group, values} -> 924 Enum.map(values, fn {key, value} -> 925 db = 926 if configs[group][key] do 927 ConfigDB.get_db_keys(configs[group][key], key) 928 end 929 930 db_value = configs[group][key] 931 932 merged_value = 933 if !is_nil(db_value) and Keyword.keyword?(db_value) and 934 ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do 935 ConfigDB.merge_group(group, key, value, db_value) 936 else 937 value 938 end 939 940 setting = %{ 941 group: ConfigDB.convert(group), 942 key: ConfigDB.convert(key), 943 value: ConfigDB.convert(merged_value) 944 } 945 946 if db, do: Map.put(setting, :db, db), else: setting 947 end) 948 end) 949 |> List.flatten() 950 951 json(conn, %{configs: merged, need_reboot: Restarter.Pleroma.need_reboot?()}) 952 end 953 end 954 955 def config_update(conn, %{"configs" => configs}) do 956 with :ok <- configurable_from_database(conn) do 957 {_errors, results} = 958 configs 959 |> Enum.filter(&whitelisted_config?/1) 960 |> Enum.map(fn 961 %{"group" => group, "key" => key, "delete" => true} = params -> 962 ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]}) 963 964 %{"group" => group, "key" => key, "value" => value} -> 965 ConfigDB.update_or_create(%{group: group, key: key, value: value}) 966 end) 967 |> Enum.split_with(fn result -> elem(result, 0) == :error end) 968 969 {deleted, updated} = 970 results 971 |> Enum.map(fn {:ok, config} -> 972 Map.put(config, :db, ConfigDB.get_db_keys(config)) 973 end) 974 |> Enum.split_with(fn config -> 975 Ecto.get_meta(config, :state) == :deleted 976 end) 977 978 Config.TransferTask.load_and_update_env(deleted, false) 979 980 if !Restarter.Pleroma.need_reboot?() do 981 changed_reboot_settings? = 982 (updated ++ deleted) 983 |> Enum.any?(fn config -> 984 group = ConfigDB.from_string(config.group) 985 key = ConfigDB.from_string(config.key) 986 value = ConfigDB.from_binary(config.value) 987 Config.TransferTask.pleroma_need_restart?(group, key, value) 988 end) 989 990 if changed_reboot_settings?, do: Restarter.Pleroma.need_reboot() 991 end 992 993 conn 994 |> put_view(ConfigView) 995 |> render("index.json", %{configs: updated, need_reboot: Restarter.Pleroma.need_reboot?()}) 996 end 997 end 998 999 def restart(conn, _params) do 1000 with :ok <- configurable_from_database(conn) do 1001 Restarter.Pleroma.restart(Config.get(:env), 50) 1002 1003 json(conn, %{}) 1004 end 1005 end 1006 1007 def need_reboot(conn, _params) do 1008 json(conn, %{need_reboot: Restarter.Pleroma.need_reboot?()}) 1009 end 1010 1011 defp configurable_from_database(conn) do 1012 if Config.get(:configurable_from_database) do 1013 :ok 1014 else 1015 errors( 1016 conn, 1017 {:error, "To use this endpoint you need to enable configuration from database."} 1018 ) 1019 end 1020 end 1021 1022 defp whitelisted_config?(group, key) do 1023 if whitelisted_configs = Config.get(:database_config_whitelist) do 1024 Enum.any?(whitelisted_configs, fn 1025 {whitelisted_group} -> 1026 group == inspect(whitelisted_group) 1027 1028 {whitelisted_group, whitelisted_key} -> 1029 group == inspect(whitelisted_group) && key == inspect(whitelisted_key) 1030 end) 1031 else 1032 true 1033 end 1034 end 1035 1036 defp whitelisted_config?(%{"group" => group, "key" => key}) do 1037 whitelisted_config?(group, key) 1038 end 1039 1040 defp whitelisted_config?(%{:group => group} = config) do 1041 whitelisted_config?(group, config[:key]) 1042 end 1043 1044 def reload_emoji(conn, _params) do 1045 Pleroma.Emoji.reload() 1046 1047 conn |> json("ok") 1048 end 1049 1050 def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do 1051 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1) 1052 1053 User.toggle_confirmation(users) 1054 1055 ModerationLog.insert_log(%{ 1056 actor: admin, 1057 subject: users, 1058 action: "confirm_email" 1059 }) 1060 1061 conn |> json("") 1062 end 1063 1064 def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do 1065 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1) 1066 1067 User.try_send_confirmation_email(users) 1068 1069 ModerationLog.insert_log(%{ 1070 actor: admin, 1071 subject: users, 1072 action: "resend_confirmation_email" 1073 }) 1074 1075 conn |> json("") 1076 end 1077 1078 def oauth_app_create(conn, params) do 1079 params = 1080 if params["name"] do 1081 Map.put(params, "client_name", params["name"]) 1082 else 1083 params 1084 end 1085 1086 result = 1087 case App.create(params) do 1088 {:ok, app} -> 1089 AppView.render("show.json", %{app: app, admin: true}) 1090 1091 {:error, changeset} -> 1092 App.errors(changeset) 1093 end 1094 1095 json(conn, result) 1096 end 1097 1098 def oauth_app_update(conn, params) do 1099 params = 1100 if params["name"] do 1101 Map.put(params, "client_name", params["name"]) 1102 else 1103 params 1104 end 1105 1106 with {:ok, app} <- App.update(params) do 1107 json(conn, AppView.render("show.json", %{app: app, admin: true})) 1108 else 1109 {:error, changeset} -> 1110 json(conn, App.errors(changeset)) 1111 1112 nil -> 1113 json_response(conn, :bad_request, "") 1114 end 1115 end 1116 1117 def oauth_app_list(conn, params) do 1118 {page, page_size} = page_params(params) 1119 1120 search_params = %{ 1121 client_name: params["name"], 1122 client_id: params["client_id"], 1123 page: page, 1124 page_size: page_size 1125 } 1126 1127 search_params = 1128 if Map.has_key?(params, "trusted") do 1129 Map.put(search_params, :trusted, params["trusted"]) 1130 else 1131 search_params 1132 end 1133 1134 with {:ok, apps, count} <- App.search(search_params) do 1135 json( 1136 conn, 1137 AppView.render("index.json", 1138 apps: apps, 1139 count: count, 1140 page_size: page_size, 1141 admin: true 1142 ) 1143 ) 1144 end 1145 end 1146 1147 def oauth_app_delete(conn, params) do 1148 with {:ok, _app} <- App.destroy(params["id"]) do 1149 json_response(conn, :no_content, "") 1150 else 1151 _ -> json_response(conn, :bad_request, "") 1152 end 1153 end 1154 1155 def stats(conn, _) do 1156 count = Stats.get_status_visibility_count() 1157 1158 conn 1159 |> json(%{"status_visibility" => count}) 1160 end 1161 1162 defp errors(conn, {:error, :not_found}) do 1163 conn 1164 |> put_status(:not_found) 1165 |> json(dgettext("errors", "Not found")) 1166 end 1167 1168 defp errors(conn, {:error, reason}) do 1169 conn 1170 |> put_status(:bad_request) 1171 |> json(reason) 1172 end 1173 1174 defp errors(conn, {:param_cast, _}) do 1175 conn 1176 |> put_status(:bad_request) 1177 |> json(dgettext("errors", "Invalid parameters")) 1178 end 1179 1180 defp errors(conn, _) do 1181 conn 1182 |> put_status(:internal_server_error) 1183 |> json(dgettext("errors", "Something went wrong")) 1184 end 1185 1186 defp page_params(params) do 1187 {get_page(params["page"]), get_page_size(params["page_size"])} 1188 end 1189 1190 defp get_page(page_string) when is_nil(page_string), do: 1 1191 1192 defp get_page(page_string) do 1193 case Integer.parse(page_string) do 1194 {page, _} -> page 1195 :error -> 1 1196 end 1197 end 1198 1199 defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size 1200 1201 defp get_page_size(page_size_string) do 1202 case Integer.parse(page_size_string) do 1203 {page_size, _} -> page_size 1204 :error -> @users_page_size 1205 end 1206 end 1207 end