Merge remote-tracking branch 'remotes/origin/develop' into relations-preloading-for-statuses-rendering
This commit is contained in:
commit
8f1d622b8d
|
@ -78,6 +78,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Mastodon API: User timelines will now respect blocks, unless you are getting the user timeline of somebody you blocked (which would be empty otherwise).
|
- Mastodon API: User timelines will now respect blocks, unless you are getting the user timeline of somebody you blocked (which would be empty otherwise).
|
||||||
- Mastodon API: Favoriting / Repeating a post multiple times will now return the identical response every time. Before, executing that action twice would return an error ("already favorited") on the second try.
|
- Mastodon API: Favoriting / Repeating a post multiple times will now return the identical response every time. Before, executing that action twice would return an error ("already favorited") on the second try.
|
||||||
- Mastodon API: Limit timeline requests to 3 per timeline per 500ms per user/ip by default.
|
- Mastodon API: Limit timeline requests to 3 per timeline per 500ms per user/ip by default.
|
||||||
|
- Admin API: `PATCH /api/pleroma/admin/users/:nickname/credentials` and `GET /api/pleroma/admin/users/:nickname/credentials`
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -414,6 +414,83 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
- `nicknames`
|
- `nicknames`
|
||||||
- Response: none (code `204`)
|
- Response: none (code `204`)
|
||||||
|
|
||||||
|
## `GET /api/pleroma/admin/users/:nickname/credentials`
|
||||||
|
|
||||||
|
### Get the user's email, password, display and settings-related fields
|
||||||
|
|
||||||
|
- Params:
|
||||||
|
- `nickname`
|
||||||
|
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"actor_type": "Person",
|
||||||
|
"allow_following_move": true,
|
||||||
|
"avatar": "https://pleroma.social/media/7e8e7508fd545ef580549b6881d80ec0ff2c81ed9ad37b9bdbbdf0e0d030159d.jpg",
|
||||||
|
"background": "https://pleroma.social/media/4de34c0bd10970d02cbdef8972bef0ebbf55f43cadc449554d4396156162fe9a.jpg",
|
||||||
|
"banner": "https://pleroma.social/media/8d92ba2bd244b613520abf557dd448adcd30f5587022813ee9dd068945986946.jpg",
|
||||||
|
"bio": "bio",
|
||||||
|
"default_scope": "public",
|
||||||
|
"discoverable": false,
|
||||||
|
"email": "user@example.com",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "example",
|
||||||
|
"value": "<a href=\"https://example.com\" rel=\"ugc\">https://example.com</a>"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hide_favorites": false,
|
||||||
|
"hide_followers": false,
|
||||||
|
"hide_followers_count": false,
|
||||||
|
"hide_follows": false,
|
||||||
|
"hide_follows_count": false,
|
||||||
|
"id": "9oouHaEEUR54hls968",
|
||||||
|
"locked": true,
|
||||||
|
"name": "user",
|
||||||
|
"no_rich_text": true,
|
||||||
|
"pleroma_settings_store": {},
|
||||||
|
"raw_fields": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "example",
|
||||||
|
"value": "https://example.com"
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"show_role": true,
|
||||||
|
"skip_thread_containment": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `PATCH /api/pleroma/admin/users/:nickname/credentials`
|
||||||
|
|
||||||
|
### Change the user's email, password, display and settings-related fields
|
||||||
|
|
||||||
|
- Params:
|
||||||
|
- `email`
|
||||||
|
- `password`
|
||||||
|
- `name`
|
||||||
|
- `bio`
|
||||||
|
- `avatar`
|
||||||
|
- `locked`
|
||||||
|
- `no_rich_text`
|
||||||
|
- `default_scope`
|
||||||
|
- `banner`
|
||||||
|
- `hide_follows`
|
||||||
|
- `hide_followers`
|
||||||
|
- `hide_followers_count`
|
||||||
|
- `hide_follows_count`
|
||||||
|
- `hide_favorites`
|
||||||
|
- `allow_following_move`
|
||||||
|
- `background`
|
||||||
|
- `show_role`
|
||||||
|
- `skip_thread_containment`
|
||||||
|
- `fields`
|
||||||
|
- `discoverable`
|
||||||
|
- `actor_type`
|
||||||
|
|
||||||
|
- Response: none (code `200`)
|
||||||
|
|
||||||
## `GET /api/pleroma/admin/reports`
|
## `GET /api/pleroma/admin/reports`
|
||||||
|
|
||||||
### Get a list of reports
|
### Get a list of reports
|
||||||
|
|
|
@ -605,6 +605,17 @@ def get_log_entry_message(%ModerationLog{
|
||||||
}"
|
}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "updated_users",
|
||||||
|
"subject" => subjects
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} updated users: #{users_to_nicknames_string(subjects)}"
|
||||||
|
end
|
||||||
|
|
||||||
defp nicknames_to_string(nicknames) do
|
defp nicknames_to_string(nicknames) do
|
||||||
nicknames
|
nicknames
|
||||||
|> Enum.map(&"@#{&1}")
|
|> Enum.map(&"@#{&1}")
|
||||||
|
|
|
@ -410,9 +410,55 @@ def update_changeset(struct, params \\ %{}) do
|
||||||
|> validate_format(:nickname, local_nickname_regex())
|
|> validate_format(:nickname, local_nickname_regex())
|
||||||
|> validate_length(:bio, max: bio_limit)
|
|> validate_length(:bio, max: bio_limit)
|
||||||
|> validate_length(:name, min: 1, max: name_limit)
|
|> validate_length(:name, min: 1, max: name_limit)
|
||||||
|
|> put_fields()
|
||||||
|
|> put_change_if_present(:bio, &{:ok, parse_bio(&1, struct)})
|
||||||
|
|> put_change_if_present(:avatar, &put_upload(&1, :avatar))
|
||||||
|
|> put_change_if_present(:banner, &put_upload(&1, :banner))
|
||||||
|
|> put_change_if_present(:background, &put_upload(&1, :background))
|
||||||
|
|> put_change_if_present(
|
||||||
|
:pleroma_settings_store,
|
||||||
|
&{:ok, Map.merge(struct.pleroma_settings_store, &1)}
|
||||||
|
)
|
||||||
|> validate_fields(false)
|
|> validate_fields(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp put_fields(changeset) do
|
||||||
|
if raw_fields = get_change(changeset, :raw_fields) do
|
||||||
|
raw_fields =
|
||||||
|
raw_fields
|
||||||
|
|> Enum.filter(fn %{"name" => n} -> n != "" end)
|
||||||
|
|
||||||
|
fields =
|
||||||
|
raw_fields
|
||||||
|
|> Enum.map(fn f -> Map.update!(f, "value", &AutoLinker.link(&1)) end)
|
||||||
|
|
||||||
|
changeset
|
||||||
|
|> put_change(:raw_fields, raw_fields)
|
||||||
|
|> put_change(:fields, fields)
|
||||||
|
else
|
||||||
|
changeset
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp put_change_if_present(changeset, map_field, value_function) do
|
||||||
|
if value = get_change(changeset, map_field) do
|
||||||
|
with {:ok, new_value} <- value_function.(value) do
|
||||||
|
put_change(changeset, map_field, new_value)
|
||||||
|
else
|
||||||
|
_ -> changeset
|
||||||
|
end
|
||||||
|
else
|
||||||
|
changeset
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp put_upload(value, type) do
|
||||||
|
with %Plug.Upload{} <- value,
|
||||||
|
{:ok, object} <- ActivityPub.upload(value, type: type) do
|
||||||
|
{:ok, object.data}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def upgrade_changeset(struct, params \\ %{}, remote? \\ false) do
|
def upgrade_changeset(struct, params \\ %{}, remote? \\ false) do
|
||||||
bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
|
bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
|
||||||
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)
|
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)
|
||||||
|
@ -456,6 +502,27 @@ def upgrade_changeset(struct, params \\ %{}, remote? \\ false) do
|
||||||
|> validate_fields(remote?)
|
|> validate_fields(remote?)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update_as_admin_changeset(struct, params) do
|
||||||
|
struct
|
||||||
|
|> update_changeset(params)
|
||||||
|
|> cast(params, [:email])
|
||||||
|
|> delete_change(:also_known_as)
|
||||||
|
|> unique_constraint(:email)
|
||||||
|
|> validate_format(:email, @email_regex)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec update_as_admin(%User{}, map) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
|
||||||
|
def update_as_admin(user, params) do
|
||||||
|
params = Map.put(params, "password_confirmation", params["password"])
|
||||||
|
changeset = update_as_admin_changeset(user, params)
|
||||||
|
|
||||||
|
if params["password"] do
|
||||||
|
reset_password(user, changeset, params)
|
||||||
|
else
|
||||||
|
User.update_and_set_cache(changeset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def password_update_changeset(struct, params) do
|
def password_update_changeset(struct, params) do
|
||||||
struct
|
struct
|
||||||
|> cast(params, [:password, :password_confirmation])
|
|> cast(params, [:password, :password_confirmation])
|
||||||
|
@ -466,10 +533,14 @@ def password_update_changeset(struct, params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec reset_password(User.t(), map) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
|
@spec reset_password(User.t(), map) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
|
||||||
def reset_password(%User{id: user_id} = user, data) do
|
def reset_password(%User{} = user, params) do
|
||||||
|
reset_password(user, user, params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def reset_password(%User{id: user_id} = user, struct, params) do
|
||||||
multi =
|
multi =
|
||||||
Multi.new()
|
Multi.new()
|
||||||
|> Multi.update(:user, password_update_changeset(user, data))
|
|> Multi.update(:user, password_update_changeset(struct, params))
|
||||||
|> Multi.delete_all(:tokens, OAuth.Token.Query.get_by_user(user_id))
|
|> Multi.delete_all(:tokens, OAuth.Token.Query.get_by_user(user_id))
|
||||||
|> Multi.delete_all(:auth, OAuth.Authorization.delete_by_user_query(user))
|
|> Multi.delete_all(:auth, OAuth.Authorization.delete_by_user_query(user))
|
||||||
|
|
||||||
|
@ -1860,6 +1931,17 @@ def fields(%{fields: nil}), do: []
|
||||||
|
|
||||||
def fields(%{fields: fields}), do: fields
|
def fields(%{fields: fields}), do: fields
|
||||||
|
|
||||||
|
def sanitized_fields(%User{} = user) do
|
||||||
|
user
|
||||||
|
|> User.fields()
|
||||||
|
|> Enum.map(fn %{"name" => name, "value" => value} ->
|
||||||
|
%{
|
||||||
|
"name" => name,
|
||||||
|
"value" => Pleroma.HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
def validate_fields(changeset, remote? \\ false) do
|
def validate_fields(changeset, remote? \\ false) do
|
||||||
limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields
|
limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields
|
||||||
limit = Pleroma.Config.get([:instance, limit_name], 0)
|
limit = Pleroma.Config.get([:instance, limit_name], 0)
|
||||||
|
|
|
@ -583,6 +583,16 @@ defp do_delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp do_delete(%Object{data: %{"type" => "Tombstone", "id" => ap_id}}, _) do
|
||||||
|
activity =
|
||||||
|
ap_id
|
||||||
|
|> Activity.Queries.by_object_id()
|
||||||
|
|> Activity.Queries.by_type("Delete")
|
||||||
|
|> Repo.one()
|
||||||
|
|
||||||
|
{:ok, activity}
|
||||||
|
end
|
||||||
|
|
||||||
@spec block(User.t(), User.t(), String.t() | nil, boolean()) ::
|
@spec block(User.t(), User.t(), String.t() | nil, boolean()) ::
|
||||||
{:ok, Activity.t()} | {:error, any()}
|
{:ok, Activity.t()} | {:error, any()}
|
||||||
def block(blocker, blocked, activity_id \\ nil, local \\ true) do
|
def block(blocker, blocked, activity_id \\ nil, local \\ true) do
|
||||||
|
|
|
@ -38,7 +38,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["read:accounts"], admin: true}
|
%{scopes: ["read:accounts"], admin: true}
|
||||||
when action in [:list_users, :user_show, :right_get]
|
when action in [:list_users, :user_show, :right_get, :show_user_credentials]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
|
@ -54,7 +54,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
:tag_users,
|
:tag_users,
|
||||||
:untag_users,
|
:untag_users,
|
||||||
:right_add,
|
:right_add,
|
||||||
:right_delete
|
:right_delete,
|
||||||
|
:update_user_credentials
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -658,6 +659,52 @@ def force_password_reset(%{assigns: %{user: admin}} = conn, %{"nicknames" => nic
|
||||||
json_response(conn, :no_content, "")
|
json_response(conn, :no_content, "")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc "Show a given user's credentials"
|
||||||
|
def show_user_credentials(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
|
||||||
|
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
|
||||||
|
conn
|
||||||
|
|> put_view(AccountView)
|
||||||
|
|> render("credentials.json", %{user: user, for: admin})
|
||||||
|
else
|
||||||
|
_ -> {:error, :not_found}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Updates a given user"
|
||||||
|
def update_user_credentials(
|
||||||
|
%{assigns: %{user: admin}} = conn,
|
||||||
|
%{"nickname" => nickname} = params
|
||||||
|
) do
|
||||||
|
with {_, user} <- {:user, User.get_cached_by_nickname(nickname)},
|
||||||
|
{:ok, _user} <-
|
||||||
|
User.update_as_admin(user, params) do
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: [user],
|
||||||
|
action: "updated_users"
|
||||||
|
})
|
||||||
|
|
||||||
|
if params["password"] do
|
||||||
|
User.force_password_reset_async(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: [user],
|
||||||
|
action: "force_password_reset"
|
||||||
|
})
|
||||||
|
|
||||||
|
json(conn, %{status: "success"})
|
||||||
|
else
|
||||||
|
{:error, changeset} ->
|
||||||
|
{_, {error, _}} = Enum.at(changeset.errors, 0)
|
||||||
|
json(conn, %{error: "New password #{error}."})
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
json(conn, %{error: "Unable to change password."})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def list_reports(conn, params) do
|
def list_reports(conn, params) do
|
||||||
{page, page_size} = page_params(params)
|
{page, page_size} = page_params(params)
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,43 @@ def render("index.json", %{users: users}) do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def render("credentials.json", %{user: user, for: for_user}) do
|
||||||
|
user = User.sanitize_html(user, User.html_filter_policy(for_user))
|
||||||
|
avatar = User.avatar_url(user) |> MediaProxy.url()
|
||||||
|
banner = User.banner_url(user) |> MediaProxy.url()
|
||||||
|
background = image_url(user.background) |> MediaProxy.url()
|
||||||
|
|
||||||
|
user
|
||||||
|
|> Map.take([
|
||||||
|
:id,
|
||||||
|
:bio,
|
||||||
|
:email,
|
||||||
|
:fields,
|
||||||
|
:name,
|
||||||
|
:nickname,
|
||||||
|
:locked,
|
||||||
|
:no_rich_text,
|
||||||
|
:default_scope,
|
||||||
|
:hide_follows,
|
||||||
|
:hide_followers_count,
|
||||||
|
:hide_follows_count,
|
||||||
|
:hide_followers,
|
||||||
|
:hide_favorites,
|
||||||
|
:allow_following_move,
|
||||||
|
:show_role,
|
||||||
|
:skip_thread_containment,
|
||||||
|
:pleroma_settings_store,
|
||||||
|
:raw_fields,
|
||||||
|
:discoverable,
|
||||||
|
:actor_type
|
||||||
|
])
|
||||||
|
|> Map.merge(%{
|
||||||
|
"avatar" => avatar,
|
||||||
|
"banner" => banner,
|
||||||
|
"background" => background
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
def render("show.json", %{user: user}) do
|
def render("show.json", %{user: user}) do
|
||||||
avatar = User.avatar_url(user) |> MediaProxy.url()
|
avatar = User.avatar_url(user) |> MediaProxy.url()
|
||||||
display_name = Pleroma.HTML.strip_tags(user.name || user.nickname)
|
display_name = Pleroma.HTML.strip_tags(user.name || user.nickname)
|
||||||
|
@ -104,4 +141,7 @@ defp parse_error(errors) do
|
||||||
""
|
""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp image_url(%{"url" => [%{"href" => href} | _]}), do: href
|
||||||
|
defp image_url(_), do: nil
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
import Pleroma.Web.ControllerHelper,
|
import Pleroma.Web.ControllerHelper,
|
||||||
only: [add_link_headers: 2, truthy_param?: 1, assign_account_by_id: 2, json_response: 3]
|
only: [add_link_headers: 2, truthy_param?: 1, assign_account_by_id: 2, json_response: 3]
|
||||||
|
|
||||||
alias Pleroma.Emoji
|
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.Plugs.RateLimiter
|
alias Pleroma.Plugs.RateLimiter
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
@ -140,17 +139,6 @@ def verify_credentials(%{assigns: %{user: user}} = conn, _) do
|
||||||
def update_credentials(%{assigns: %{user: original_user}} = conn, params) do
|
def update_credentials(%{assigns: %{user: original_user}} = conn, params) do
|
||||||
user = original_user
|
user = original_user
|
||||||
|
|
||||||
params =
|
|
||||||
if Map.has_key?(params, "fields_attributes") do
|
|
||||||
Map.update!(params, "fields_attributes", fn fields ->
|
|
||||||
fields
|
|
||||||
|> normalize_fields_attributes()
|
|
||||||
|> Enum.filter(fn %{"name" => n} -> n != "" end)
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
params
|
|
||||||
end
|
|
||||||
|
|
||||||
user_params =
|
user_params =
|
||||||
[
|
[
|
||||||
:no_rich_text,
|
:no_rich_text,
|
||||||
|
@ -169,46 +157,20 @@ def update_credentials(%{assigns: %{user: original_user}} = conn, params) do
|
||||||
add_if_present(acc, params, to_string(key), key, &{:ok, truthy_param?(&1)})
|
add_if_present(acc, params, to_string(key), key, &{:ok, truthy_param?(&1)})
|
||||||
end)
|
end)
|
||||||
|> add_if_present(params, "display_name", :name)
|
|> add_if_present(params, "display_name", :name)
|
||||||
|> add_if_present(params, "note", :bio, fn value -> {:ok, User.parse_bio(value, user)} end)
|
|> add_if_present(params, "note", :bio)
|
||||||
|> add_if_present(params, "avatar", :avatar, fn value ->
|
|> add_if_present(params, "avatar", :avatar)
|
||||||
with %Plug.Upload{} <- value,
|
|> add_if_present(params, "header", :banner)
|
||||||
{:ok, object} <- ActivityPub.upload(value, type: :avatar) do
|
|> add_if_present(params, "pleroma_background_image", :background)
|
||||||
{:ok, object.data}
|
|> add_if_present(
|
||||||
end
|
params,
|
||||||
end)
|
"fields_attributes",
|
||||||
|> add_if_present(params, "header", :banner, fn value ->
|
:raw_fields,
|
||||||
with %Plug.Upload{} <- value,
|
&{:ok, normalize_fields_attributes(&1)}
|
||||||
{:ok, object} <- ActivityPub.upload(value, type: :banner) do
|
)
|
||||||
{:ok, object.data}
|
|> add_if_present(params, "pleroma_settings_store", :pleroma_settings_store)
|
||||||
end
|
|
||||||
end)
|
|
||||||
|> add_if_present(params, "pleroma_background_image", :background, fn value ->
|
|
||||||
with %Plug.Upload{} <- value,
|
|
||||||
{:ok, object} <- ActivityPub.upload(value, type: :background) do
|
|
||||||
{:ok, object.data}
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|> add_if_present(params, "fields_attributes", :fields, fn fields ->
|
|
||||||
fields = Enum.map(fields, fn f -> Map.update!(f, "value", &AutoLinker.link(&1)) end)
|
|
||||||
|
|
||||||
{:ok, fields}
|
|
||||||
end)
|
|
||||||
|> add_if_present(params, "fields_attributes", :raw_fields)
|
|
||||||
|> add_if_present(params, "pleroma_settings_store", :pleroma_settings_store, fn value ->
|
|
||||||
{:ok, Map.merge(user.pleroma_settings_store, value)}
|
|
||||||
end)
|
|
||||||
|> add_if_present(params, "default_scope", :default_scope)
|
|> add_if_present(params, "default_scope", :default_scope)
|
||||||
|> add_if_present(params, "actor_type", :actor_type)
|
|> add_if_present(params, "actor_type", :actor_type)
|
||||||
|
|
||||||
emojis_text = (user_params["display_name"] || "") <> (user_params["note"] || "")
|
|
||||||
|
|
||||||
user_emojis =
|
|
||||||
user
|
|
||||||
|> Map.get(:emoji, [])
|
|
||||||
|> Enum.concat(Emoji.Formatter.get_emoji_map(emojis_text))
|
|
||||||
|> Enum.dedup()
|
|
||||||
|
|
||||||
user_params = Map.put(user_params, :emoji, user_emojis)
|
|
||||||
changeset = User.update_changeset(user, user_params)
|
changeset = User.update_changeset(user, user_params)
|
||||||
|
|
||||||
with {:ok, user} <- User.update_and_set_cache(changeset) do
|
with {:ok, user} <- User.update_and_set_cache(changeset) do
|
||||||
|
|
|
@ -194,7 +194,7 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
fields: user.fields,
|
fields: user.fields,
|
||||||
bot: bot,
|
bot: bot,
|
||||||
source: %{
|
source: %{
|
||||||
note: Pleroma.HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")),
|
note: (user.bio || "") |> String.replace(~r(<br */?>), "\n") |> Pleroma.HTML.strip_tags(),
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
fields: user.raw_fields,
|
fields: user.raw_fields,
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
|
|
|
@ -173,6 +173,8 @@ defmodule Pleroma.Web.Router do
|
||||||
|
|
||||||
get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
|
get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
|
||||||
patch("/users/force_password_reset", AdminAPIController, :force_password_reset)
|
patch("/users/force_password_reset", AdminAPIController, :force_password_reset)
|
||||||
|
get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials)
|
||||||
|
patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials)
|
||||||
|
|
||||||
get("/users", AdminAPIController, :list_users)
|
get("/users", AdminAPIController, :list_users)
|
||||||
get("/users/:nickname", AdminAPIController, :user_show)
|
get("/users/:nickname", AdminAPIController, :user_show)
|
||||||
|
|
|
@ -1425,6 +1425,12 @@ test "it creates a delete activity and deletes the original object" do
|
||||||
assert Repo.get(Object, object.id).data["type"] == "Tombstone"
|
assert Repo.get(Object, object.id).data["type"] == "Tombstone"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it doesn't fail when an activity was already deleted" do
|
||||||
|
{:ok, delete} = insert(:note_activity) |> Object.normalize() |> ActivityPub.delete()
|
||||||
|
|
||||||
|
assert {:ok, ^delete} = delete |> Object.normalize() |> ActivityPub.delete()
|
||||||
|
end
|
||||||
|
|
||||||
test "decrements user note count only for public activities" do
|
test "decrements user note count only for public activities" do
|
||||||
user = insert(:user, note_count: 10)
|
user = insert(:user, note_count: 10)
|
||||||
|
|
||||||
|
|
|
@ -3374,6 +3374,75 @@ test "returns log filtered by search", %{conn: conn, moderator: moderator} do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "GET /users/:nickname/credentials" do
|
||||||
|
test "gets the user credentials", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
|
||||||
|
|
||||||
|
response = assert json_response(conn, 200)
|
||||||
|
assert response["email"] == user.email
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 403 if requested by a non-admin" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/pleroma/admin/users/#{user.nickname}/credentials")
|
||||||
|
|
||||||
|
assert json_response(conn, :forbidden)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "PATCH /users/:nickname/credentials" do
|
||||||
|
test "changes password and email", %{conn: conn, admin: admin} do
|
||||||
|
user = insert(:user)
|
||||||
|
assert user.password_reset_pending == false
|
||||||
|
|
||||||
|
conn =
|
||||||
|
patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
|
||||||
|
"password" => "new_password",
|
||||||
|
"email" => "new_email@example.com",
|
||||||
|
"name" => "new_name"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{"status" => "success"}
|
||||||
|
|
||||||
|
ObanHelpers.perform_all()
|
||||||
|
|
||||||
|
updated_user = User.get_by_id(user.id)
|
||||||
|
|
||||||
|
assert updated_user.email == "new_email@example.com"
|
||||||
|
assert updated_user.name == "new_name"
|
||||||
|
assert updated_user.password_hash != user.password_hash
|
||||||
|
assert updated_user.password_reset_pending == true
|
||||||
|
|
||||||
|
[log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort()
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry1) ==
|
||||||
|
"@#{admin.nickname} updated users: @#{user.nickname}"
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry2) ==
|
||||||
|
"@#{admin.nickname} forced password reset for users: @#{user.nickname}"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 403 if requested by a non-admin" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{
|
||||||
|
"password" => "new_password",
|
||||||
|
"email" => "new_email@example.com",
|
||||||
|
"name" => "new_name"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response(conn, :forbidden)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "PATCH /users/:nickname/force_password_reset" do
|
describe "PATCH /users/:nickname/force_password_reset" do
|
||||||
test "sets password_reset_pending to true", %{conn: conn} do
|
test "sets password_reset_pending to true", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
|
@ -76,7 +76,7 @@ test "updates the user's bio", %{conn: conn} do
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
patch(conn, "/api/v1/accounts/update_credentials", %{
|
patch(conn, "/api/v1/accounts/update_credentials", %{
|
||||||
"note" => "I drink #cofe with @#{user2.nickname}"
|
"note" => "I drink #cofe with @#{user2.nickname}\n\nsuya.."
|
||||||
})
|
})
|
||||||
|
|
||||||
assert user_data = json_response(conn, 200)
|
assert user_data = json_response(conn, 200)
|
||||||
|
@ -84,7 +84,7 @@ test "updates the user's bio", %{conn: conn} do
|
||||||
assert user_data["note"] ==
|
assert user_data["note"] ==
|
||||||
~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe">#cofe</a> with <span class="h-card"><a data-user="#{
|
~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe">#cofe</a> with <span class="h-card"><a data-user="#{
|
||||||
user2.id
|
user2.id
|
||||||
}" class="u-url mention" href="#{user2.ap_id}" rel="ugc">@<span>#{user2.nickname}</span></a></span>)
|
}" class="u-url mention" href="#{user2.ap_id}" rel="ugc">@<span>#{user2.nickname}</span></a></span><br/><br/>suya..)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "updates the user's locking status", %{conn: conn} do
|
test "updates the user's locking status", %{conn: conn} do
|
||||||
|
|
|
@ -32,7 +32,8 @@ test "Represent a user account" do
|
||||||
background: background_image,
|
background: background_image,
|
||||||
nickname: "shp@shitposter.club",
|
nickname: "shp@shitposter.club",
|
||||||
name: ":karjalanpiirakka: shp",
|
name: ":karjalanpiirakka: shp",
|
||||||
bio: "<script src=\"invalid-html\"></script><span>valid html</span>",
|
bio:
|
||||||
|
"<script src=\"invalid-html\"></script><span>valid html</span>. a<br>b<br/>c<br >d<br />f",
|
||||||
inserted_at: ~N[2017-08-15 15:47:06.597036]
|
inserted_at: ~N[2017-08-15 15:47:06.597036]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -46,7 +47,7 @@ test "Represent a user account" do
|
||||||
followers_count: 3,
|
followers_count: 3,
|
||||||
following_count: 0,
|
following_count: 0,
|
||||||
statuses_count: 5,
|
statuses_count: 5,
|
||||||
note: "<span>valid html</span>",
|
note: "<span>valid html</span>. a<br/>b<br/>c<br/>d<br/>f",
|
||||||
url: user.ap_id,
|
url: user.ap_id,
|
||||||
avatar: "http://localhost:4001/images/avi.png",
|
avatar: "http://localhost:4001/images/avi.png",
|
||||||
avatar_static: "http://localhost:4001/images/avi.png",
|
avatar_static: "http://localhost:4001/images/avi.png",
|
||||||
|
@ -63,7 +64,7 @@ test "Represent a user account" do
|
||||||
fields: [],
|
fields: [],
|
||||||
bot: false,
|
bot: false,
|
||||||
source: %{
|
source: %{
|
||||||
note: "valid html",
|
note: "valid html. a\nb\nc\nd\nf",
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
actor_type: "Person",
|
actor_type: "Person",
|
||||||
|
|
Loading…
Reference in a new issue