diff options
| -rw-r--r-- | docs/Admin-API.md | 23 | ||||
| -rw-r--r-- | lib/pleroma/user.ex | 84 | ||||
| -rw-r--r-- | lib/pleroma/web/admin_api/admin_api_controller.ex | 36 | ||||
| -rw-r--r-- | lib/pleroma/web/admin_api/search.ex | 54 | ||||
| -rw-r--r-- | test/user_test.exs | 18 | ||||
| -rw-r--r-- | test/web/admin_api/admin_api_controller_test.exs | 40 | ||||
| -rw-r--r-- | test/web/admin_api/search_test.exs | 88 | 
7 files changed, 256 insertions, 87 deletions
| diff --git a/docs/Admin-API.md b/docs/Admin-API.md index 2edb31f3c..84adca6ff 100644 --- a/docs/Admin-API.md +++ b/docs/Admin-API.md @@ -8,10 +8,15 @@ Authentication is required and the user must be an admin.  - Method `GET`  - Query Params: -  - `query`: **string** *optional* search term -  - `local_only`: **bool** *optional* whether to return only local users -  - `page`: **integer** *optional* page number -  - `page_size`: **integer** *optional* number of users per page (default is `50`) +  - *optional* `query`: **string** search term +  - *optional* `filters`: **string** comma-separated string of filters: +    - `local`: only local users +    - `external`: only external users +    - `active`: only active users +    - `deactivated`: only deactivated users +  - *optional* `page`: **integer** page number +  - *optional* `page_size`: **integer** number of users per page (default is `50`) +- Example: `https://mypleroma.org/api/pleroma/admin/users?query=john&filters=local,active&page=1&page_size=10`  - Response:  ```JSON @@ -22,7 +27,13 @@ Authentication is required and the user must be an admin.      {        "deactivated": bool,        "id": integer, -      "nickname": string +      "nickname": string, +      "roles": { +        "admin": bool, +        "moderator": bool +      }, +      "local": bool, +      "tags": array      },      ...    ] @@ -99,7 +110,7 @@ Authentication is required and the user must be an admin.  Note: Available `:permission_group` is currently moderator and admin. 404 is returned when the permission group doesn’t exist. -### Get user user permission groups membership +### Get user user permission groups membership per permission group  - Method: `GET`  - Params: none diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 3beebd121..728b00a56 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -768,52 +768,6 @@ defmodule Pleroma.User do      Repo.all(query)    end -  @spec search_for_admin(%{ -          local: boolean(), -          page: number(), -          page_size: number() -        }) :: {:ok, [Pleroma.User.t()], number()} -  def search_for_admin(%{query: nil, local: local, page: page, page_size: page_size}) do -    query = -      from(u in User, order_by: u.nickname) -      |> maybe_local_user_query(local) - -    paginated_query = -      query -      |> paginate(page, page_size) - -    count = -      query -      |> Repo.aggregate(:count, :id) - -    {:ok, Repo.all(paginated_query), count} -  end - -  @spec search_for_admin(%{ -          query: binary(), -          local: boolean(), -          page: number(), -          page_size: number() -        }) :: {:ok, [Pleroma.User.t()], number()} -  def search_for_admin(%{ -        query: term, -        local: local, -        page: page, -        page_size: page_size -      }) do -    maybe_local_query = User |> maybe_local_user_query(local) - -    search_query = from(u in maybe_local_query, where: ilike(u.nickname, ^"%#{term}%")) -    count = search_query |> Repo.aggregate(:count, :id) - -    results = -      search_query -      |> paginate(page, page_size) -      |> Repo.all() - -    {:ok, results, count} -  end -    def search(query, resolve \\ false, for_user \\ nil) do      # Strip the beginning @ off if there is a query      query = String.trim_leading(query, "@") @@ -852,7 +806,7 @@ defmodule Pleroma.User do          search_rank:            fragment(              """ -             CASE WHEN (?) THEN (?) * 1.3  +             CASE WHEN (?) THEN (?) * 1.3               WHEN (?) THEN (?) * 1.2               WHEN (?) THEN (?) * 1.1               ELSE (?) END @@ -1067,6 +1021,42 @@ defmodule Pleroma.User do      )    end +  def maybe_external_user_query(query, external) do +    if external, do: external_user_query(query), else: query +  end + +  def external_user_query(query \\ User) do +    from( +      u in query, +      where: u.local == false, +      where: not is_nil(u.nickname) +    ) +  end + +  def maybe_active_user_query(query, active) do +    if active, do: active_user_query(query), else: query +  end + +  def active_user_query(query \\ User) do +    from( +      u in query, +      where: fragment("not (?->'deactivated' @> 'true')", u.info), +      where: not is_nil(u.nickname) +    ) +  end + +  def maybe_deactivated_user_query(query, deactivated) do +    if deactivated, do: deactivated_user_query(query), else: query +  end + +  def deactivated_user_query(query \\ User) do +    from( +      u in query, +      where: fragment("(?->'deactivated' @> 'true')", u.info), +      where: not is_nil(u.nickname) +    ) +  end +    def active_local_user_query do      from(        u in local_user_query(), diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 6d9bf2895..3fa9c6909 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -3,17 +3,18 @@  # SPDX-License-Identifier: AGPL-3.0-only  defmodule Pleroma.Web.AdminAPI.AdminAPIController do -  @users_page_size 50 -    use Pleroma.Web, :controller    alias Pleroma.User    alias Pleroma.Web.ActivityPub.Relay    alias Pleroma.Web.AdminAPI.AccountView +  alias Pleroma.Web.AdminAPI.Search    import Pleroma.Web.ControllerHelper, only: [json_response: 3]    require Logger +  @users_page_size 50 +    action_fallback(:errors)    def user_delete(conn, %{"nickname" => nickname}) do @@ -63,17 +64,17 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do           do: json_response(conn, :no_content, "")    end -  def list_users(%{assigns: %{user: admin}} = conn, params) do +  def list_users(conn, params) do      {page, page_size} = page_params(params) +    filters = maybe_parse_filters(params["filters"]) + +    search_params = %{ +      query: params["query"], +      page: page, +      page_size: page_size +    } -    with {:ok, users, count} <- -           User.search_for_admin(%{ -             query: params["query"], -             admin: admin, -             local: params["local_only"] == "true", -             page: page, -             page_size: page_size -           }), +    with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)),           do:             conn             |> json( @@ -85,6 +86,19 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do             )    end +  @filters ~w(local external active deactivated) + +  defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{} + +  @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{} +  defp maybe_parse_filters(filters) do +    filters +    |> String.split(",") +    |> Enum.filter(&Enum.member?(@filters, &1)) +    |> Enum.map(&String.to_atom(&1)) +    |> Enum.into(%{}, &{&1, true}) +  end +    def right_add(conn, %{"permission_group" => permission_group, "nickname" => nickname})        when permission_group in ["moderator", "admin"] do      user = User.get_by_nickname(nickname) diff --git a/lib/pleroma/web/admin_api/search.ex b/lib/pleroma/web/admin_api/search.ex new file mode 100644 index 000000000..9a8e41c2a --- /dev/null +++ b/lib/pleroma/web/admin_api/search.ex @@ -0,0 +1,54 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.Search do +  import Ecto.Query + +  alias Pleroma.Repo +  alias Pleroma.User + +  @page_size 50 + +  def user(%{query: term} = params) when is_nil(term) or term == "" do +    query = maybe_filtered_query(params) + +    paginated_query = +      maybe_filtered_query(params) +      |> paginate(params[:page] || 1, params[:page_size] || @page_size) + +    count = query |> Repo.aggregate(:count, :id) + +    results = Repo.all(paginated_query) + +    {:ok, results, count} +  end + +  def user(%{query: term} = params) when is_binary(term) do +    search_query = from(u in maybe_filtered_query(params), where: ilike(u.nickname, ^"%#{term}%")) + +    count = search_query |> Repo.aggregate(:count, :id) + +    results = +      search_query +      |> paginate(params[:page] || 1, params[:page_size] || @page_size) +      |> Repo.all() + +    {:ok, results, count} +  end + +  defp maybe_filtered_query(params) do +    from(u in User, order_by: u.nickname) +    |> User.maybe_local_user_query(params[:local]) +    |> User.maybe_external_user_query(params[:external]) +    |> User.maybe_active_user_query(params[:active]) +    |> User.maybe_deactivated_user_query(params[:deactivated]) +  end + +  defp paginate(query, page, page_size) do +    from(u in query, +      limit: ^page_size, +      offset: ^((page - 1) * page_size) +    ) +  end +end diff --git a/test/user_test.exs b/test/user_test.exs index 442599910..8cf2ba6ab 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -8,6 +8,7 @@ defmodule Pleroma.UserTest do    alias Pleroma.Repo    alias Pleroma.User    alias Pleroma.Web.CommonAPI +    use Pleroma.DataCase    import Pleroma.Factory @@ -1107,21 +1108,4 @@ defmodule Pleroma.UserTest do      assert {:ok, user_state3} = User.bookmark(user, id2)      assert user_state3.bookmarks == [id2]    end - -  describe "search for admin" do -    test "it ignores case" do -      insert(:user, nickname: "papercoach") -      insert(:user, nickname: "CanadaPaperCoach") - -      {:ok, _results, count} = -        User.search_for_admin(%{ -          query: "paper", -          local: false, -          page: 1, -          page_size: 50 -        }) - -      assert count == 2 -    end -  end  end diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index 0aab7f262..7da237eca 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -408,13 +408,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do      test "regular search with page size" do        admin = insert(:user, info: %{is_admin: true}) -      user = insert(:user, nickname: "bob") -      user2 = insert(:user, nickname: "bo") +      user = insert(:user, nickname: "aalice") +      user2 = insert(:user, nickname: "alice")        conn =          build_conn()          |> assign(:user, admin) -        |> get("/api/pleroma/admin/users?query=bo&page_size=1&page=1") +        |> get("/api/pleroma/admin/users?query=a&page_size=1&page=1")        assert json_response(conn, 200) == %{                 "count" => 2, @@ -434,7 +434,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do        conn =          build_conn()          |> assign(:user, admin) -        |> get("/api/pleroma/admin/users?query=bo&page_size=1&page=2") +        |> get("/api/pleroma/admin/users?query=a&page_size=1&page=2")        assert json_response(conn, 200) == %{                 "count" => 2, @@ -461,7 +461,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do        conn =          build_conn()          |> assign(:user, admin) -        |> get("/api/pleroma/admin/users?query=bo&local_only=true") +        |> get("/api/pleroma/admin/users?query=bo&filters=local")        assert json_response(conn, 200) == %{                 "count" => 1, @@ -488,7 +488,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do        conn =          build_conn()          |> assign(:user, admin) -        |> get("/api/pleroma/admin/users?local_only=true") +        |> get("/api/pleroma/admin/users?filters=local")        assert json_response(conn, 200) == %{                 "count" => 2, @@ -513,6 +513,34 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do                 ]               }      end + +    test "it works with multiple filters" do +      admin = insert(:user, nickname: "john", info: %{is_admin: true}) +      user = insert(:user, nickname: "bob", local: false, info: %{deactivated: true}) + +      insert(:user, nickname: "ken", local: true, info: %{deactivated: true}) +      insert(:user, nickname: "bobb", local: false, info: %{deactivated: false}) + +      conn = +        build_conn() +        |> assign(:user, admin) +        |> get("/api/pleroma/admin/users?filters=deactivated,external") + +      assert json_response(conn, 200) == %{ +               "count" => 1, +               "page_size" => 50, +               "users" => [ +                 %{ +                   "deactivated" => user.info.deactivated, +                   "id" => user.id, +                   "nickname" => user.nickname, +                   "roles" => %{"admin" => false, "moderator" => false}, +                   "local" => user.local, +                   "tags" => [] +                 } +               ] +             } +    end    end    test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation" do diff --git a/test/web/admin_api/search_test.exs b/test/web/admin_api/search_test.exs new file mode 100644 index 000000000..3950996ed --- /dev/null +++ b/test/web/admin_api/search_test.exs @@ -0,0 +1,88 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.SearchTest do +  use Pleroma.Web.ConnCase + +  alias Pleroma.Web.AdminAPI.Search + +  import Pleroma.Factory + +  describe "search for admin" do +    test "it ignores case" do +      insert(:user, nickname: "papercoach") +      insert(:user, nickname: "CanadaPaperCoach") + +      {:ok, _results, count} = +        Search.user(%{ +          query: "paper", +          local: false, +          page: 1, +          page_size: 50 +        }) + +      assert count == 2 +    end + +    test "it returns local/external users" do +      insert(:user, local: true) +      insert(:user, local: false) +      insert(:user, local: false) + +      {:ok, _results, local_count} = +        Search.user(%{ +          query: "", +          local: true +        }) + +      {:ok, _results, external_count} = +        Search.user(%{ +          query: "", +          external: true +        }) + +      assert local_count == 1 +      assert external_count == 2 +    end + +    test "it returns active/deactivated users" do +      insert(:user, info: %{deactivated: true}) +      insert(:user, info: %{deactivated: true}) +      insert(:user, info: %{deactivated: false}) + +      {:ok, _results, active_count} = +        Search.user(%{ +          query: "", +          active: true +        }) + +      {:ok, _results, deactivated_count} = +        Search.user(%{ +          query: "", +          deactivated: true +        }) + +      assert active_count == 1 +      assert deactivated_count == 2 +    end + +    test "it returns specific user" do +      insert(:user) +      insert(:user) +      insert(:user, nickname: "bob", local: true, info: %{deactivated: false}) + +      {:ok, _results, total_count} = Search.user(%{query: ""}) + +      {:ok, _results, count} = +        Search.user(%{ +          query: "Bo", +          active: true, +          local: true +        }) + +      assert total_count == 3 +      assert count == 1 +    end +  end +end | 
