diff --git a/docs/development/API/admin_api.md b/docs/development/API/admin_api.md index ef04944af..a59804c87 100644 --- a/docs/development/API/admin_api.md +++ b/docs/development/API/admin_api.md @@ -80,7 +80,7 @@ The `/api/v1/pleroma/admin/*` path is backwards compatible with `/api/pleroma/ad { `nickname`, `email`, - `password` (optional) + `password` (optional; when provided, must not be empty) } ] - Response: Array of user objects. When a user is created without a `password`, `data` diff --git a/lib/pleroma/web/admin_api/controllers/user_controller.ex b/lib/pleroma/web/admin_api/controllers/user_controller.ex index c94964937..f0edfe4ba 100644 --- a/lib/pleroma/web/admin_api/controllers/user_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/user_controller.ex @@ -153,7 +153,7 @@ defmodule Pleroma.Web.AdminAPI.UserController do _ ) do conn = - if Enum.any?(users, fn user -> Map.get(user, :password) in ["", nil] end) do + if Enum.any?(users, fn user -> not Map.has_key?(user, :password) end) do put_private(conn, :skip_idempotency_cache, true) else conn @@ -162,15 +162,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do multi = users |> Enum.map(fn %{nickname: nickname, email: email} = attrs -> - passwordless? = Map.get(attrs, :password) in ["", nil] + {password, passwordless?} = + case Map.fetch(attrs, :password) do + {:ok, password} -> + {password, false} - password = - case Map.get(attrs, :password) do - password when is_binary(password) and password != "" -> - password - - _ -> - :crypto.strong_rand_bytes(16) |> Base.encode64() + :error -> + {:crypto.strong_rand_bytes(16) |> Base.encode64(), true} end user_data = %{ diff --git a/lib/pleroma/web/api_spec/operations/admin/user_operation.ex b/lib/pleroma/web/api_spec/operations/admin/user_operation.ex index 84cf789d8..7c4725f56 100644 --- a/lib/pleroma/web/api_spec/operations/admin/user_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/user_operation.ex @@ -86,7 +86,8 @@ defmodule Pleroma.Web.ApiSpec.Admin.UserOperation do type: :string, description: "Optional. When omitted, a `password_reset_link` is returned, " <> - "allowing the user to set their own password." + "allowing the user to set their own password. When provided, " <> + "it must not be empty." } }, required: [:nickname, :email] 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 32f2c1d30..0bea0c185 100644 --- a/test/pleroma/web/admin_api/controllers/user_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/user_controller_test.exs @@ -208,6 +208,21 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do refute Map.has_key?(account2["data"], "password_reset_link") end + test "Explicit empty passwords return a conflict", %{conn: conn} do + conn = + conn + |> put_req_header("accept", "application/json") + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/users", %{ + "users" => [ + %{"nickname" => "lain", "email" => "lain@example.org", "password" => ""} + ] + }) + + assert [%{"error" => "password can't be blank"}] = json_response(conn, 409) + refute User.get_by_nickname("lain") + end + test "Password reset token failures roll back the user batch", %{conn: conn} do with_mock Pleroma.PasswordResetToken, [:passthrough], create_token: fn user ->