diff options
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | lib/pleroma/user.ex | 7 | ||||
| -rw-r--r-- | lib/pleroma/web/admin_api/controllers/user_controller.ex | 128 | ||||
| -rw-r--r-- | lib/pleroma/web/admin_api/views/account_view.ex | 19 | ||||
| -rw-r--r-- | lib/pleroma/web/api_spec.ex | 5 | ||||
| -rw-r--r-- | lib/pleroma/web/api_spec/operations/admin/user_operation.ex | 389 | ||||
| -rw-r--r-- | lib/pleroma/web/router.ex | 2 | ||||
| -rw-r--r-- | test/pleroma/web/admin_api/controllers/user_controller_test.exs | 118 | 
8 files changed, 542 insertions, 127 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index ed08701fd..50484aaef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).  <details>    <summary>API Changes</summary>  - Admin API: (`GET /api/pleroma/admin/users`) filter users by `unconfirmed` status and `actor_type`. +- Admin API: OpenAPI spec for the user-related operations  - Pleroma API: `GET /api/v2/pleroma/chats` added. It is exactly like `GET /api/v1/pleroma/chats` except supports pagination.  - Pleroma API: Add `idempotency_key` to the chat message entity that can be used for optimistic message sending.  - Pleroma API: (`GET /api/v1/pleroma/federation_status`) Add a way to get a list of unreachable instances. diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 9942617d8..c1aa0f716 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -2255,13 +2255,6 @@ defmodule Pleroma.User do      |> update_and_set_cache()    end -  def roles(%{is_moderator: is_moderator, is_admin: is_admin}) do -    %{ -      admin: is_admin, -      moderator: is_moderator -    } -  end -    def validate_fields(changeset, remote? \\ false) do      limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields      limit = Config.get([:instance, limit_name], 0) diff --git a/lib/pleroma/web/admin_api/controllers/user_controller.ex b/lib/pleroma/web/admin_api/controllers/user_controller.ex index 65bc63cb9..d3e4c18a3 100644 --- a/lib/pleroma/web/admin_api/controllers/user_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/user_controller.ex @@ -13,16 +13,17 @@ defmodule Pleroma.Web.AdminAPI.UserController do    alias Pleroma.Web.ActivityPub.Builder    alias Pleroma.Web.ActivityPub.Pipeline    alias Pleroma.Web.AdminAPI -  alias Pleroma.Web.AdminAPI.AccountView    alias Pleroma.Web.AdminAPI.Search    alias Pleroma.Web.Plugs.OAuthScopesPlug    @users_page_size 50 +  plug(Pleroma.Web.ApiSpec.CastAndValidate) +    plug(      OAuthScopesPlug,      %{scopes: ["admin:read:accounts"]} -    when action in [:list, :show] +    when action in [:index, :show]    )    plug( @@ -44,13 +45,19 @@ defmodule Pleroma.Web.AdminAPI.UserController do      when action in [:follow, :unfollow]    ) +  plug(:put_view, Pleroma.Web.AdminAPI.AccountView) +    action_fallback(AdminAPI.FallbackController) -  def delete(conn, %{"nickname" => nickname}) do -    delete(conn, %{"nicknames" => [nickname]}) +  defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.UserOperation + +  def delete(conn, %{nickname: nickname}) do +    conn +    |> Map.put(:body_params, %{nicknames: [nickname]}) +    |> delete(%{})    end -  def delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do +  def delete(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do      users = Enum.map(nicknames, &User.get_cached_by_nickname/1)      Enum.each(users, fn user -> @@ -67,10 +74,16 @@ defmodule Pleroma.Web.AdminAPI.UserController do      json(conn, nicknames)    end -  def follow(%{assigns: %{user: admin}} = conn, %{ -        "follower" => follower_nick, -        "followed" => followed_nick -      }) do +  def follow( +        %{ +          assigns: %{user: admin}, +          body_params: %{ +            follower: follower_nick, +            followed: followed_nick +          } +        } = conn, +        _ +      ) do      with %User{} = follower <- User.get_cached_by_nickname(follower_nick),           %User{} = followed <- User.get_cached_by_nickname(followed_nick) do        User.follow(follower, followed) @@ -86,10 +99,16 @@ defmodule Pleroma.Web.AdminAPI.UserController do      json(conn, "ok")    end -  def unfollow(%{assigns: %{user: admin}} = conn, %{ -        "follower" => follower_nick, -        "followed" => followed_nick -      }) do +  def unfollow( +        %{ +          assigns: %{user: admin}, +          body_params: %{ +            follower: follower_nick, +            followed: followed_nick +          } +        } = conn, +        _ +      ) do      with %User{} = follower <- User.get_cached_by_nickname(follower_nick),           %User{} = followed <- User.get_cached_by_nickname(followed_nick) do        User.unfollow(follower, followed) @@ -105,9 +124,10 @@ defmodule Pleroma.Web.AdminAPI.UserController do      json(conn, "ok")    end -  def create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do +  def create(%{assigns: %{user: admin}, body_params: %{users: users}} = conn, _) do      changesets = -      Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} -> +      users +      |> Enum.map(fn %{nickname: nickname, email: email, password: password} ->          user_data = %{            nickname: nickname,            name: nickname, @@ -124,52 +144,49 @@ defmodule Pleroma.Web.AdminAPI.UserController do        end)      case Pleroma.Repo.transaction(changesets) do -      {:ok, users} -> -        res = -          users +      {:ok, users_map} -> +        users = +          users_map            |> Map.values()            |> Enum.map(fn user ->              {:ok, user} = User.post_register_action(user)              user            end) -          |> Enum.map(&AccountView.render("created.json", %{user: &1}))          ModerationLog.insert_log(%{            actor: admin, -          subjects: Map.values(users), +          subjects: users,            action: "create"          }) -        json(conn, res) +        render(conn, "created_many.json", users: users)        {:error, id, changeset, _} -> -        res = +        changesets =            Enum.map(changesets.operations, fn -            {current_id, {:changeset, _current_changeset, _}} when current_id == id -> -              AccountView.render("create-error.json", %{changeset: changeset}) +            {^id, {:changeset, _current_changeset, _}} -> +              changeset              {_, {:changeset, current_changeset, _}} -> -              AccountView.render("create-error.json", %{changeset: current_changeset}) +              current_changeset            end)          conn          |> put_status(:conflict) -        |> json(res) +        |> render("create_errors.json", changesets: changesets)      end    end -  def show(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do +  def show(%{assigns: %{user: admin}} = conn, %{nickname: nickname}) do      with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do -      conn -      |> put_view(AccountView) -      |> render("show.json", %{user: user}) +      render(conn, "show.json", %{user: user})      else        _ -> {:error, :not_found}      end    end -  def toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do +  def toggle_activation(%{assigns: %{user: admin}} = conn, %{nickname: nickname}) do      user = User.get_cached_by_nickname(nickname)      {:ok, updated_user} = User.set_activation(user, !user.is_active) @@ -182,12 +199,10 @@ defmodule Pleroma.Web.AdminAPI.UserController do        action: action      }) -    conn -    |> put_view(AccountView) -    |> render("show.json", %{user: updated_user}) +    render(conn, "show.json", user: updated_user)    end -  def activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do +  def activate(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do      users = Enum.map(nicknames, &User.get_cached_by_nickname/1)      {:ok, updated_users} = User.set_activation(users, true) @@ -197,12 +212,10 @@ defmodule Pleroma.Web.AdminAPI.UserController do        action: "activate"      }) -    conn -    |> put_view(AccountView) -    |> render("index.json", %{users: Keyword.values(updated_users)}) +    render(conn, "index.json", users: Keyword.values(updated_users))    end -  def deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do +  def deactivate(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do      users = Enum.map(nicknames, &User.get_cached_by_nickname/1)      {:ok, updated_users} = User.set_activation(users, false) @@ -212,12 +225,10 @@ defmodule Pleroma.Web.AdminAPI.UserController do        action: "deactivate"      }) -    conn -    |> put_view(AccountView) -    |> render("index.json", %{users: Keyword.values(updated_users)}) +    render(conn, "index.json", users: Keyword.values(updated_users))    end -  def approve(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do +  def approve(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do      users = Enum.map(nicknames, &User.get_cached_by_nickname/1)      {:ok, updated_users} = User.approve(users) @@ -227,36 +238,27 @@ defmodule Pleroma.Web.AdminAPI.UserController do        action: "approve"      }) -    conn -    |> put_view(AccountView) -    |> render("index.json", %{users: updated_users}) +    render(conn, "index.json", users: updated_users)    end -  def list(conn, params) do +  def index(conn, params) do      {page, page_size} = page_params(params) -    filters = maybe_parse_filters(params["filters"]) +    filters = maybe_parse_filters(params[:filters])      search_params =        %{ -        query: params["query"], +        query: params[:query],          page: page,          page_size: page_size, -        tags: params["tags"], -        name: params["name"], -        email: params["email"], -        actor_types: params["actor_types"] +        tags: params[:tags], +        name: params[:name], +        email: params[:email], +        actor_types: params[:actor_types]        }        |> Map.merge(filters)      with {:ok, users, count} <- Search.user(search_params) do -      json( -        conn, -        AccountView.render("index.json", -          users: users, -          count: count, -          page_size: page_size -        ) -      ) +      render(conn, "index.json", users: users, count: count, page_size: page_size)      end    end @@ -274,8 +276,8 @@ defmodule Pleroma.Web.AdminAPI.UserController do    defp page_params(params) do      { -      fetch_integer_param(params, "page", 1), -      fetch_integer_param(params, "page_size", @users_page_size) +      fetch_integer_param(params, :page, 1), +      fetch_integer_param(params, :page_size, @users_page_size)      }    end  end diff --git a/lib/pleroma/web/admin_api/views/account_view.ex b/lib/pleroma/web/admin_api/views/account_view.ex index d7c63d385..e053a9b67 100644 --- a/lib/pleroma/web/admin_api/views/account_view.ex +++ b/lib/pleroma/web/admin_api/views/account_view.ex @@ -75,7 +75,7 @@ defmodule Pleroma.Web.AdminAPI.AccountView do        "display_name" => display_name,        "is_active" => user.is_active,        "local" => user.local, -      "roles" => User.roles(user), +      "roles" => roles(user),        "tags" => user.tags || [],        "is_confirmed" => user.is_confirmed,        "is_approved" => user.is_approved, @@ -85,6 +85,10 @@ defmodule Pleroma.Web.AdminAPI.AccountView do      }    end +  def render("created_many.json", %{users: users}) do +    render_many(users, AccountView, "created.json", as: :user) +  end +    def render("created.json", %{user: user}) do      %{        type: "success", @@ -96,7 +100,11 @@ defmodule Pleroma.Web.AdminAPI.AccountView do      }    end -  def render("create-error.json", %{changeset: %Ecto.Changeset{changes: changes, errors: errors}}) do +  def render("create_errors.json", %{changesets: changesets}) do +    render_many(changesets, AccountView, "create_error.json", as: :changeset) +  end + +  def render("create_error.json", %{changeset: %Ecto.Changeset{changes: changes, errors: errors}}) do      %{        type: "error",        code: 409, @@ -140,4 +148,11 @@ defmodule Pleroma.Web.AdminAPI.AccountView do    defp image_url(%{"url" => [%{"href" => href} | _]}), do: href    defp image_url(_), do: nil + +  defp roles(%{is_moderator: is_moderator, is_admin: is_admin}) do +    %{ +      admin: is_admin, +      moderator: is_moderator +    } +  end  end diff --git a/lib/pleroma/web/api_spec.ex b/lib/pleroma/web/api_spec.ex index adc8762dc..528cd9cf4 100644 --- a/lib/pleroma/web/api_spec.ex +++ b/lib/pleroma/web/api_spec.ex @@ -92,9 +92,10 @@ defmodule Pleroma.Web.ApiSpec do                "Invites",                "MediaProxy cache",                "OAuth application managment", -              "Report managment",                "Relays", -              "Status administration" +              "Report managment", +              "Status administration", +              "User administration"              ]            },            %{"name" => "Applications", "tags" => ["Applications", "Push subscriptions"]}, diff --git a/lib/pleroma/web/api_spec/operations/admin/user_operation.ex b/lib/pleroma/web/api_spec/operations/admin/user_operation.ex new file mode 100644 index 000000000..c9d0bfd7c --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/admin/user_operation.ex @@ -0,0 +1,389 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Admin.UserOperation do +  alias OpenApiSpex.Operation +  alias OpenApiSpex.Schema +  alias Pleroma.Web.ApiSpec.Schemas.ActorType +  alias Pleroma.Web.ApiSpec.Schemas.ApiError + +  import Pleroma.Web.ApiSpec.Helpers + +  def open_api_operation(action) do +    operation = String.to_existing_atom("#{action}_operation") +    apply(__MODULE__, operation, []) +  end + +  def index_operation do +    %Operation{ +      tags: ["User administration"], +      summary: "List users", +      operationId: "AdminAPI.UserController.index", +      security: [%{"oAuth" => ["admin:read:accounts"]}], +      parameters: [ +        Operation.parameter(:filters, :query, :string, "Comma separated list of filters"), +        Operation.parameter(:query, :query, :string, "Search users query"), +        Operation.parameter(:name, :query, :string, "Search by display name"), +        Operation.parameter(:email, :query, :string, "Search by email"), +        Operation.parameter(:page, :query, :integer, "Page Number"), +        Operation.parameter(:page_size, :query, :integer, "Number of users to return per page"), +        Operation.parameter( +          :actor_types, +          :query, +          %Schema{type: :array, items: ActorType}, +          "Filter by actor type" +        ), +        Operation.parameter( +          :tags, +          :query, +          %Schema{type: :array, items: %Schema{type: :string}}, +          "Filter by tags" +        ) +        | admin_api_params() +      ], +      responses: %{ +        200 => +          Operation.response( +            "Response", +            "application/json", +            %Schema{ +              type: :object, +              properties: %{ +                users: %Schema{type: :array, items: user()}, +                count: %Schema{type: :integer}, +                page_size: %Schema{type: :integer} +              } +            } +          ), +        403 => Operation.response("Forbidden", "application/json", ApiError) +      } +    } +  end + +  def create_operation do +    %Operation{ +      tags: ["User administration"], +      summary: "Create a single or multiple users", +      operationId: "AdminAPI.UserController.create", +      security: [%{"oAuth" => ["admin:write:accounts"]}], +      parameters: admin_api_params(), +      requestBody: +        request_body( +          "Parameters", +          %Schema{ +            description: "POST body for creating users", +            type: :object, +            properties: %{ +              users: %Schema{ +                type: :array, +                items: %Schema{ +                  type: :object, +                  properties: %{ +                    nickname: %Schema{type: :string}, +                    email: %Schema{type: :string}, +                    password: %Schema{type: :string} +                  } +                } +              } +            } +          } +        ), +      responses: %{ +        200 => +          Operation.response("Response", "application/json", %Schema{ +            type: :array, +            items: %Schema{ +              type: :object, +              properties: %{ +                code: %Schema{type: :integer}, +                type: %Schema{type: :string}, +                data: %Schema{ +                  type: :object, +                  properties: %{ +                    email: %Schema{type: :string, format: :email}, +                    nickname: %Schema{type: :string} +                  } +                } +              } +            } +          }), +        403 => Operation.response("Forbidden", "application/json", ApiError), +        409 => +          Operation.response("Conflict", "application/json", %Schema{ +            type: :array, +            items: %Schema{ +              type: :object, +              properties: %{ +                code: %Schema{type: :integer}, +                error: %Schema{type: :string}, +                type: %Schema{type: :string}, +                data: %Schema{ +                  type: :object, +                  properties: %{ +                    email: %Schema{type: :string, format: :email}, +                    nickname: %Schema{type: :string} +                  } +                } +              } +            } +          }) +      } +    } +  end + +  def show_operation do +    %Operation{ +      tags: ["User administration"], +      summary: "Show user", +      operationId: "AdminAPI.UserController.show", +      security: [%{"oAuth" => ["admin:read:accounts"]}], +      parameters: [ +        Operation.parameter( +          :nickname, +          :path, +          :string, +          "User nickname or ID" +        ) +        | admin_api_params() +      ], +      responses: %{ +        200 => Operation.response("Response", "application/json", user()), +        403 => Operation.response("Forbidden", "application/json", ApiError), +        404 => Operation.response("Not Found", "application/json", ApiError) +      } +    } +  end + +  def follow_operation do +    %Operation{ +      tags: ["User administration"], +      summary: "Follow", +      operationId: "AdminAPI.UserController.follow", +      security: [%{"oAuth" => ["admin:write:follows"]}], +      parameters: admin_api_params(), +      requestBody: +        request_body( +          "Parameters", +          %Schema{ +            type: :object, +            properties: %{ +              follower: %Schema{type: :string, description: "Follower nickname"}, +              followed: %Schema{type: :string, description: "Followed nickname"} +            } +          } +        ), +      responses: %{ +        200 => Operation.response("Response", "application/json", %Schema{type: :string}), +        403 => Operation.response("Forbidden", "application/json", ApiError) +      } +    } +  end + +  def unfollow_operation do +    %Operation{ +      tags: ["User administration"], +      summary: "Unfollow", +      operationId: "AdminAPI.UserController.unfollow", +      security: [%{"oAuth" => ["admin:write:follows"]}], +      parameters: admin_api_params(), +      requestBody: +        request_body( +          "Parameters", +          %Schema{ +            type: :object, +            properties: %{ +              follower: %Schema{type: :string, description: "Follower nickname"}, +              followed: %Schema{type: :string, description: "Followed nickname"} +            } +          } +        ), +      responses: %{ +        200 => Operation.response("Response", "application/json", %Schema{type: :string}), +        403 => Operation.response("Forbidden", "application/json", ApiError) +      } +    } +  end + +  def approve_operation do +    %Operation{ +      tags: ["User administration"], +      summary: "Approve multiple users", +      operationId: "AdminAPI.UserController.approve", +      security: [%{"oAuth" => ["admin:write:accounts"]}], +      parameters: admin_api_params(), +      requestBody: +        request_body( +          "Parameters", +          %Schema{ +            description: "POST body for deleting multiple users", +            type: :object, +            properties: %{ +              nicknames: %Schema{ +                type: :array, +                items: %Schema{type: :string} +              } +            } +          } +        ), +      responses: %{ +        200 => +          Operation.response("Response", "application/json", %Schema{ +            type: :object, +            properties: %{user: %Schema{type: :array, items: user()}} +          }), +        403 => Operation.response("Forbidden", "application/json", ApiError) +      } +    } +  end + +  def toggle_activation_operation do +    %Operation{ +      tags: ["User administration"], +      summary: "Toggle user activation", +      operationId: "AdminAPI.UserController.toggle_activation", +      security: [%{"oAuth" => ["admin:write:accounts"]}], +      parameters: [ +        Operation.parameter(:nickname, :path, :string, "User nickname") +        | admin_api_params() +      ], +      responses: %{ +        200 => Operation.response("Response", "application/json", user()), +        403 => Operation.response("Forbidden", "application/json", ApiError) +      } +    } +  end + +  def activate_operation do +    %Operation{ +      tags: ["User administration"], +      summary: "Activate multiple users", +      operationId: "AdminAPI.UserController.activate", +      security: [%{"oAuth" => ["admin:write:accounts"]}], +      parameters: admin_api_params(), +      requestBody: +        request_body( +          "Parameters", +          %Schema{ +            description: "POST body for deleting multiple users", +            type: :object, +            properties: %{ +              nicknames: %Schema{ +                type: :array, +                items: %Schema{type: :string} +              } +            } +          } +        ), +      responses: %{ +        200 => +          Operation.response("Response", "application/json", %Schema{ +            type: :object, +            properties: %{user: %Schema{type: :array, items: user()}} +          }), +        403 => Operation.response("Forbidden", "application/json", ApiError) +      } +    } +  end + +  def deactivate_operation do +    %Operation{ +      tags: ["User administration"], +      summary: "Deactivates multiple users", +      operationId: "AdminAPI.UserController.deactivate", +      security: [%{"oAuth" => ["admin:write:accounts"]}], +      parameters: admin_api_params(), +      requestBody: +        request_body( +          "Parameters", +          %Schema{ +            description: "POST body for deleting multiple users", +            type: :object, +            properties: %{ +              nicknames: %Schema{ +                type: :array, +                items: %Schema{type: :string} +              } +            } +          } +        ), +      responses: %{ +        200 => +          Operation.response("Response", "application/json", %Schema{ +            type: :object, +            properties: %{user: %Schema{type: :array, items: user()}} +          }), +        403 => Operation.response("Forbidden", "application/json", ApiError) +      } +    } +  end + +  def delete_operation do +    %Operation{ +      tags: ["User administration"], +      summary: "Removes a single or multiple users", +      operationId: "AdminAPI.UserController.delete", +      security: [%{"oAuth" => ["admin:write:accounts"]}], +      parameters: [ +        Operation.parameter( +          :nickname, +          :query, +          :string, +          "User nickname" +        ) +        | admin_api_params() +      ], +      requestBody: +        request_body( +          "Parameters", +          %Schema{ +            description: "POST body for deleting multiple users", +            type: :object, +            properties: %{ +              nicknames: %Schema{ +                type: :array, +                items: %Schema{type: :string} +              } +            } +          } +        ), +      responses: %{ +        200 => +          Operation.response("Response", "application/json", %Schema{ +            description: "Array of nicknames", +            type: :array, +            items: %Schema{type: :string} +          }), +        403 => Operation.response("Forbidden", "application/json", ApiError) +      } +    } +  end + +  defp user do +    %Schema{ +      type: :object, +      properties: %{ +        id: %Schema{type: :string}, +        email: %Schema{type: :string, format: :email}, +        avatar: %Schema{type: :string, format: :uri}, +        nickname: %Schema{type: :string}, +        display_name: %Schema{type: :string}, +        is_active: %Schema{type: :boolean}, +        local: %Schema{type: :boolean}, +        roles: %Schema{ +          type: :object, +          properties: %{ +            admin: %Schema{type: :boolean}, +            moderator: %Schema{type: :boolean} +          } +        }, +        tags: %Schema{type: :array, items: %Schema{type: :string}}, +        is_confirmed: %Schema{type: :boolean}, +        is_approved: %Schema{type: :boolean}, +        url: %Schema{type: :string, format: :uri}, +        registration_reason: %Schema{type: :string, nullable: true}, +        actor_type: %Schema{type: :string} +      } +    } +  end +end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 72ad14f05..de0bd27d7 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -204,7 +204,7 @@ defmodule Pleroma.Web.Router do      get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials)      patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials) -    get("/users", UserController, :list) +    get("/users", UserController, :index)      get("/users/:nickname", UserController, :show)      get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)      get("/users/:nickname/chats", AdminAPIController, :list_user_chats) diff --git a/test/pleroma/web/admin_api/controllers/user_controller_test.exs b/test/pleroma/web/admin_api/controllers/user_controller_test.exs index beb8a5d58..31319b5e5 100644 --- a/test/pleroma/web/admin_api/controllers/user_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/user_controller_test.exs @@ -44,7 +44,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do      conn = get(build_conn(), "/api/pleroma/admin/users/#{user.nickname}?admin_token=password123") -    assert json_response(conn, 200) +    assert json_response_and_validate_schema(conn, 200)    end    test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope", @@ -67,7 +67,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do          |> assign(:token, good_token)          |> get(url) -      assert json_response(conn, 200) +      assert json_response_and_validate_schema(conn, 200)      end      for good_token <- [good_token1, good_token2, good_token3] do @@ -87,7 +87,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do          |> assign(:token, bad_token)          |> get(url) -      assert json_response(conn, :forbidden) +      assert json_response_and_validate_schema(conn, :forbidden)      end    end @@ -131,7 +131,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do          assert ModerationLog.get_log_entry_message(log_entry) ==                   "@#{admin.nickname} deleted users: @#{user.nickname}" -        assert json_response(conn, 200) == [user.nickname] +        assert json_response_and_validate_schema(conn, 200) == [user.nickname]          user = Repo.get(User, user.id)          refute user.is_active @@ -152,28 +152,30 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        user_one = insert(:user)        user_two = insert(:user) -      conn = +      response =          conn          |> put_req_header("accept", "application/json") +        |> put_req_header("content-type", "application/json")          |> delete("/api/pleroma/admin/users", %{            nicknames: [user_one.nickname, user_two.nickname]          }) +        |> json_response_and_validate_schema(200)        log_entry = Repo.one(ModerationLog)        assert ModerationLog.get_log_entry_message(log_entry) ==                 "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}" -      response = json_response(conn, 200)        assert response -- [user_one.nickname, user_two.nickname] == []      end    end    describe "/api/pleroma/admin/users" do      test "Create", %{conn: conn} do -      conn = +      response =          conn          |> put_req_header("accept", "application/json") +        |> put_req_header("content-type", "application/json")          |> post("/api/pleroma/admin/users", %{            "users" => [              %{ @@ -188,8 +190,9 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do              }            ]          }) +        |> json_response_and_validate_schema(200) +        |> Enum.map(&Map.get(&1, "type")) -      response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))        assert response == ["success", "success"]        log_entry = Repo.one(ModerationLog) @@ -203,6 +206,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn =          conn          |> put_req_header("accept", "application/json") +        |> put_req_header("content-type", "application/json")          |> post("/api/pleroma/admin/users", %{            "users" => [              %{ @@ -213,7 +217,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do            ]          }) -      assert json_response(conn, 409) == [ +      assert json_response_and_validate_schema(conn, 409) == [                 %{                   "code" => 409,                   "data" => %{ @@ -232,6 +236,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn =          conn          |> put_req_header("accept", "application/json") +        |> put_req_header("content-type", "application/json")          |> post("/api/pleroma/admin/users", %{            "users" => [              %{ @@ -242,7 +247,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do            ]          }) -      assert json_response(conn, 409) == [ +      assert json_response_and_validate_schema(conn, 409) == [                 %{                   "code" => 409,                   "data" => %{ @@ -261,6 +266,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn =          conn          |> put_req_header("accept", "application/json") +        |> put_req_header("content-type", "application/json")          |> post("/api/pleroma/admin/users", %{            "users" => [              %{ @@ -276,7 +282,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do            ]          }) -      assert json_response(conn, 409) == [ +      assert json_response_and_validate_schema(conn, 409) == [                 %{                   "code" => 409,                   "data" => %{ @@ -307,7 +313,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}") -      assert user_response(user) == json_response(conn, 200) +      assert user_response(user) == json_response_and_validate_schema(conn, 200)      end      test "when the user doesn't exist", %{conn: conn} do @@ -315,7 +321,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}") -      assert %{"error" => "Not found"} == json_response(conn, 404) +      assert %{"error" => "Not found"} == json_response_and_validate_schema(conn, 404)      end    end @@ -326,6 +332,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn        |> put_req_header("accept", "application/json") +      |> put_req_header("content-type", "application/json")        |> post("/api/pleroma/admin/users/follow", %{          "follower" => follower.nickname,          "followed" => user.nickname @@ -352,6 +359,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn        |> put_req_header("accept", "application/json") +      |> put_req_header("content-type", "application/json")        |> post("/api/pleroma/admin/users/unfollow", %{          "follower" => follower.nickname,          "followed" => user.nickname @@ -395,7 +403,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do          ]          |> Enum.sort_by(& &1["nickname"]) -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 3,                 "page_size" => 50,                 "users" => users @@ -410,7 +418,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        assert %{"count" => 26, "page_size" => 10, "users" => users1} =                 conn                 |> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"}) -               |> json_response(200) +               |> json_response_and_validate_schema(200)        assert Enum.count(users1) == 10        assert service1 not in users1 @@ -418,7 +426,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        assert %{"count" => 26, "page_size" => 10, "users" => users2} =                 conn                 |> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"}) -               |> json_response(200) +               |> json_response_and_validate_schema(200)        assert Enum.count(users2) == 10        assert service1 not in users2 @@ -426,7 +434,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        assert %{"count" => 26, "page_size" => 10, "users" => users3} =                 conn                 |> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"}) -               |> json_response(200) +               |> json_response_and_validate_schema(200)        assert Enum.count(users3) == 6        assert service1 not in users3 @@ -437,7 +445,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn = get(conn, "/api/pleroma/admin/users?page=2") -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 2,                 "page_size" => 50,                 "users" => [] @@ -449,7 +457,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn = get(conn, "/api/pleroma/admin/users?query=bo") -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 1,                 "page_size" => 50,                 "users" => [user_response(user, %{"local" => true})] @@ -462,7 +470,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn = get(conn, "/api/pleroma/admin/users?query=domain.com") -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 1,                 "page_size" => 50,                 "users" => [user_response(user)] @@ -475,7 +483,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com") -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 1,                 "page_size" => 50,                 "users" => [user_response(user)] @@ -488,7 +496,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn = get(conn, "/api/pleroma/admin/users?name=display") -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 1,                 "page_size" => 50,                 "users" => [user_response(user)] @@ -501,7 +509,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn = get(conn, "/api/pleroma/admin/users?email=email@example.com") -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 1,                 "page_size" => 50,                 "users" => [user_response(user)] @@ -514,7 +522,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1") -      assert json_response(conn1, 200) == %{ +      assert json_response_and_validate_schema(conn1, 200) == %{                 "count" => 2,                 "page_size" => 1,                 "users" => [user_response(user)] @@ -522,7 +530,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2") -      assert json_response(conn2, 200) == %{ +      assert json_response_and_validate_schema(conn2, 200) == %{                 "count" => 2,                 "page_size" => 1,                 "users" => [user_response(user2)] @@ -542,7 +550,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do          |> assign(:token, token)          |> get("/api/pleroma/admin/users?query=bo&filters=local") -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 1,                 "page_size" => 50,                 "users" => [user_response(user)] @@ -570,7 +578,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do          ]          |> Enum.sort_by(& &1["nickname"]) -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 3,                 "page_size" => 50,                 "users" => users @@ -587,7 +595,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        result =          conn          |> get("/api/pleroma/admin/users?filters=unconfirmed") -        |> json_response(200) +        |> json_response_and_validate_schema(200)        users =          Enum.map([old_user, sad_user], fn user -> @@ -620,7 +628,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do          )        ] -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 1,                 "page_size" => 50,                 "users" => users @@ -647,7 +655,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do          ]          |> Enum.sort_by(& &1["nickname"]) -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 2,                 "page_size" => 50,                 "users" => users @@ -661,7 +669,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator") -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 1,                 "page_size" => 50,                 "users" => [ @@ -682,8 +690,8 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        response =          conn -        |> get(user_path(conn, :list), %{actor_types: ["Person"]}) -        |> json_response(200) +        |> get(user_path(conn, :index), %{actor_types: ["Person"]}) +        |> json_response_and_validate_schema(200)        users =          [ @@ -705,8 +713,8 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        response =          conn -        |> get(user_path(conn, :list), %{actor_types: ["Person", "Service"]}) -        |> json_response(200) +        |> get(user_path(conn, :index), %{actor_types: ["Person", "Service"]}) +        |> json_response_and_validate_schema(200)        users =          [ @@ -728,8 +736,8 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        response =          conn -        |> get(user_path(conn, :list), %{actor_types: ["Service"]}) -        |> json_response(200) +        |> get(user_path(conn, :index), %{actor_types: ["Service"]}) +        |> json_response_and_validate_schema(200)        users = [user_response(user_service, %{"actor_type" => "Service"})] @@ -751,7 +759,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do          ]          |> Enum.sort_by(& &1["nickname"]) -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 2,                 "page_size" => 50,                 "users" => users @@ -776,7 +784,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do                   %{"id" => ^admin_id},                   %{"id" => ^user_id}                 ] -             } = json_response(conn, 200) +             } = json_response_and_validate_schema(conn, 200)      end      test "it works with multiple filters" do @@ -793,7 +801,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do          |> assign(:token, token)          |> get("/api/pleroma/admin/users?filters=deactivated,external") -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 1,                 "page_size" => 50,                 "users" => [user_response(user)] @@ -805,7 +813,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do        conn = get(conn, "/api/pleroma/admin/users") -      assert json_response(conn, 200) == %{ +      assert json_response_and_validate_schema(conn, 200) == %{                 "count" => 1,                 "page_size" => 50,                 "users" => [ @@ -820,13 +828,14 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do      user_two = insert(:user, is_active: false)      conn = -      patch( -        conn, +      conn +      |> put_req_header("content-type", "application/json") +      |> patch(          "/api/pleroma/admin/users/activate",          %{nicknames: [user_one.nickname, user_two.nickname]}        ) -    response = json_response(conn, 200) +    response = json_response_and_validate_schema(conn, 200)      assert Enum.map(response["users"], & &1["is_active"]) == [true, true]      log_entry = Repo.one(ModerationLog) @@ -840,13 +849,14 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do      user_two = insert(:user, is_active: true)      conn = -      patch( -        conn, +      conn +      |> put_req_header("content-type", "application/json") +      |> patch(          "/api/pleroma/admin/users/deactivate",          %{nicknames: [user_one.nickname, user_two.nickname]}        ) -    response = json_response(conn, 200) +    response = json_response_and_validate_schema(conn, 200)      assert Enum.map(response["users"], & &1["is_active"]) == [false, false]      log_entry = Repo.one(ModerationLog) @@ -860,13 +870,14 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do      user_two = insert(:user, is_approved: false)      conn = -      patch( -        conn, +      conn +      |> put_req_header("content-type", "application/json") +      |> patch(          "/api/pleroma/admin/users/approve",          %{nicknames: [user_one.nickname, user_two.nickname]}        ) -    response = json_response(conn, 200) +    response = json_response_and_validate_schema(conn, 200)      assert Enum.map(response["users"], & &1["is_approved"]) == [true, true]      log_entry = Repo.one(ModerationLog) @@ -878,9 +889,12 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do    test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do      user = insert(:user) -    conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation") +    conn = +      conn +      |> put_req_header("content-type", "application/json") +      |> patch("/api/pleroma/admin/users/#{user.nickname}/toggle_activation") -    assert json_response(conn, 200) == +    assert json_response_and_validate_schema(conn, 200) ==               user_response(                 user,                 %{"is_active" => !user.is_active}  | 
