Allow creating accounts without a password in the AdminAPI, update docs
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
parent
b0ae451942
commit
36bc7b116d
6 changed files with 114 additions and 19 deletions
1
changelog.d/admin-api-passwordless-accounts.add
Normal file
1
changelog.d/admin-api-passwordless-accounts.add
Normal file
|
|
@ -0,0 +1 @@
|
|||
Allow creating accounts without a password in the AdminAPI
|
||||
|
|
@ -80,10 +80,24 @@ The `/api/v1/pleroma/admin/*` path is backwards compatible with `/api/pleroma/ad
|
|||
{
|
||||
`nickname`,
|
||||
`email`,
|
||||
`password`
|
||||
`password` (optional)
|
||||
}
|
||||
]
|
||||
- Response: Array of user objects
|
||||
- Response: Array of user objects. When a user is created without a `password`, `data`
|
||||
object contains a `password_reset_link` the user can follow to set their own password.
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"data": {
|
||||
"nickname": string,
|
||||
"email": string,
|
||||
"password_reset_link": string (optional)
|
||||
},
|
||||
"type": "success"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## `POST /api/v1/pleroma/admin/users/follow`
|
||||
|
||||
|
|
|
|||
|
|
@ -8,13 +8,16 @@ defmodule Pleroma.Web.AdminAPI.UserController do
|
|||
import Pleroma.Web.ControllerHelper,
|
||||
only: [fetch_integer_param: 3]
|
||||
|
||||
alias Ecto.Multi
|
||||
alias Pleroma.ModerationLog
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Builder
|
||||
alias Pleroma.Web.ActivityPub.Pipeline
|
||||
alias Pleroma.Web.AdminAPI
|
||||
alias Pleroma.Web.AdminAPI.Search
|
||||
alias Pleroma.Web.Endpoint
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Web.Router
|
||||
|
||||
@users_page_size 50
|
||||
|
||||
|
|
@ -148,9 +151,24 @@ defmodule Pleroma.Web.AdminAPI.UserController do
|
|||
} = conn,
|
||||
_
|
||||
) do
|
||||
changesets =
|
||||
# Nicknames of users for which a password reset link will be generated.
|
||||
passwordless_nicknames =
|
||||
users
|
||||
|> Enum.map(fn %{nickname: nickname, email: email, password: password} ->
|
||||
|> Enum.filter(fn user -> Map.get(user, :password) in ["", nil] end)
|
||||
|> Enum.map(& &1.nickname)
|
||||
|
||||
multi =
|
||||
users
|
||||
|> Enum.map(fn %{nickname: nickname, email: email} = attrs ->
|
||||
password =
|
||||
case Map.get(attrs, :password) do
|
||||
password when is_binary(password) and password != "" ->
|
||||
password
|
||||
|
||||
_ ->
|
||||
:crypto.strong_rand_bytes(16) |> Base.encode64()
|
||||
end
|
||||
|
||||
user_data = %{
|
||||
nickname: nickname,
|
||||
name: nickname,
|
||||
|
|
@ -162,11 +180,11 @@ defmodule Pleroma.Web.AdminAPI.UserController do
|
|||
|
||||
User.register_changeset(%User{}, user_data, need_confirmation: false)
|
||||
end)
|
||||
|> Enum.reduce(Ecto.Multi.new(), fn changeset, multi ->
|
||||
Ecto.Multi.insert(multi, Ecto.UUID.generate(), changeset)
|
||||
|> Enum.reduce(Multi.new(), fn changeset, multi ->
|
||||
Multi.insert(multi, "user-" <> changeset.changes.nickname, changeset)
|
||||
end)
|
||||
|
||||
case Pleroma.Repo.transaction(changesets) do
|
||||
case Pleroma.Repo.transaction(multi) do
|
||||
{:ok, users_map} ->
|
||||
users =
|
||||
users_map
|
||||
|
|
@ -177,17 +195,28 @@ defmodule Pleroma.Web.AdminAPI.UserController do
|
|||
user
|
||||
end)
|
||||
|
||||
password_reset_links =
|
||||
users
|
||||
|> Enum.filter(fn user -> Enum.member?(passwordless_nicknames, user.nickname) end)
|
||||
|> Map.new(fn user ->
|
||||
{:ok, token} = Pleroma.PasswordResetToken.create_token(user)
|
||||
{user.id, Router.Helpers.reset_password_url(Endpoint, :reset, token.token)}
|
||||
end)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
subjects: users,
|
||||
action: "create"
|
||||
})
|
||||
|
||||
render(conn, "created_many.json", users: users)
|
||||
render(conn, "created_many.json",
|
||||
users: users,
|
||||
password_reset_links: password_reset_links
|
||||
)
|
||||
|
||||
{:error, id, changeset, _} ->
|
||||
changesets =
|
||||
Enum.map(changesets.operations, fn
|
||||
Enum.map(multi.operations, fn
|
||||
{^id, {:changeset, _current_changeset, _}} ->
|
||||
changeset
|
||||
|
||||
|
|
|
|||
|
|
@ -88,18 +88,29 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
|
|||
}
|
||||
end
|
||||
|
||||
def render("created_many.json", %{users: users}) do
|
||||
render_many(users, AccountView, "created.json", as: :user)
|
||||
def render("created_many.json", %{users: users} = assigns) do
|
||||
password_reset_links = Map.get(assigns, :password_reset_links, %{})
|
||||
|
||||
render_many(users, AccountView, "created.json",
|
||||
as: :user,
|
||||
password_reset_links: password_reset_links
|
||||
)
|
||||
end
|
||||
|
||||
def render("created.json", %{user: user}) do
|
||||
%{
|
||||
type: "success",
|
||||
code: 200,
|
||||
data: %{
|
||||
def render("created.json", %{user: user} = assigns) do
|
||||
password_reset_links = Map.get(assigns, :password_reset_links, %{})
|
||||
|
||||
data =
|
||||
%{
|
||||
nickname: user.nickname,
|
||||
email: user.email
|
||||
}
|
||||
|> maybe_put_password_reset_link(Map.get(password_reset_links, user.id))
|
||||
|
||||
%{
|
||||
type: "success",
|
||||
code: 200,
|
||||
data: data
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -119,6 +130,11 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
|
|||
}
|
||||
end
|
||||
|
||||
defp maybe_put_password_reset_link(data, nil), do: data
|
||||
|
||||
defp maybe_put_password_reset_link(data, link),
|
||||
do: Map.put(data, :password_reset_link, link)
|
||||
|
||||
def merge_account_views(%User{} = user) do
|
||||
MastodonAPI.AccountView.render("show.json", %{user: user, skip_visibility_check: true})
|
||||
|> Map.merge(AdminAPI.AccountView.render("show.json", %{user: user}))
|
||||
|
|
|
|||
|
|
@ -82,8 +82,14 @@ defmodule Pleroma.Web.ApiSpec.Admin.UserOperation do
|
|||
properties: %{
|
||||
nickname: %Schema{type: :string},
|
||||
email: %Schema{type: :string},
|
||||
password: %Schema{type: :string}
|
||||
}
|
||||
password: %Schema{
|
||||
type: :string,
|
||||
description:
|
||||
"Optional. When omitted, a `password_reset_link` is returned, " <>
|
||||
"allowing the user to set their own password."
|
||||
}
|
||||
},
|
||||
required: [:nickname, :email]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -102,7 +108,8 @@ defmodule Pleroma.Web.ApiSpec.Admin.UserOperation do
|
|||
type: :object,
|
||||
properties: %{
|
||||
email: %Schema{type: :string, format: :email},
|
||||
nickname: %Schema{type: :string}
|
||||
nickname: %Schema{type: :string},
|
||||
password_reset_link: %Schema{type: :string}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,6 +176,34 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
|
|||
assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
|
||||
end
|
||||
|
||||
test "Creating without a password returns a password reset link", %{conn: conn} do
|
||||
[account1, account2] =
|
||||
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"
|
||||
},
|
||||
%{
|
||||
"nickname" => "lain2",
|
||||
"email" => "lain2@example.org",
|
||||
"password" => "dupa.8"
|
||||
}
|
||||
]
|
||||
})
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert account1["type"] == "success"
|
||||
assert %{"password_reset_link" => link} = account1["data"]
|
||||
assert link =~ "/api/v1/pleroma/password_reset/"
|
||||
|
||||
assert account2["type"] == "success"
|
||||
refute Map.has_key?(account2["data"], "password_reset_link")
|
||||
end
|
||||
|
||||
test "Cannot create user with existing email", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue