forked from AkkomaGang/akkoma
Merge branch 'feature/multiple-users-activation-permissions' into 'develop'
Ability to toggle activation status, permission group & delete multiple users Closes admin-fe#39 See merge request pleroma/pleroma!1825
This commit is contained in:
commit
21f0757b0d
9 changed files with 361 additions and 124 deletions
|
@ -18,6 +18,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Metadata Link: Atom syndication Feed
|
- Metadata Link: Atom syndication Feed
|
||||||
- Mix task to re-count statuses for all users (`mix pleroma.count_statuses`)
|
- Mix task to re-count statuses for all users (`mix pleroma.count_statuses`)
|
||||||
- Mastodon API: Add `exclude_visibilities` parameter to the timeline and notification endpoints
|
- Mastodon API: Add `exclude_visibilities` parameter to the timeline and notification endpoints
|
||||||
|
- Admin API: `/users/:nickname/toggle_activation` endpoint is now deprecated in favor of: `/users/activate`, `/users/deactivate`, both accept `nicknames` array
|
||||||
|
- Admin API: `POST/DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group` are deprecated in favor of: `POST/DELETE /api/pleroma/admin/users/permission_group/:permission_group` (both accept `nicknames` array), `DELETE /api/pleroma/admin/users` (`nickname` query param or `nickname` sent in JSON body) is deprecated in favor of: `DELETE /api/pleroma/admin/users` (`nicknames` query array param or `nicknames` sent in JSON body).
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
|
- **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
|
||||||
|
|
|
@ -47,7 +47,7 @@ Authentication is required and the user must be an admin.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## `/api/pleroma/admin/users`
|
## DEPRECATED `DELETE /api/pleroma/admin/users`
|
||||||
|
|
||||||
### Remove a user
|
### Remove a user
|
||||||
|
|
||||||
|
@ -56,6 +56,15 @@ Authentication is required and the user must be an admin.
|
||||||
- `nickname`
|
- `nickname`
|
||||||
- Response: User’s nickname
|
- Response: User’s nickname
|
||||||
|
|
||||||
|
## `DELETE /api/pleroma/admin/users`
|
||||||
|
|
||||||
|
### Remove a user
|
||||||
|
|
||||||
|
- Method `DELETE`
|
||||||
|
- Params:
|
||||||
|
- `nicknames`
|
||||||
|
- Response: Array of user nicknames
|
||||||
|
|
||||||
### Create a user
|
### Create a user
|
||||||
|
|
||||||
- Method: `POST`
|
- Method: `POST`
|
||||||
|
@ -154,28 +163,86 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Add user in permission group
|
## DEPRECATED `POST /api/pleroma/admin/users/:nickname/permission_group/:permission_group`
|
||||||
|
|
||||||
|
### Add user to permission group
|
||||||
|
|
||||||
- Method: `POST`
|
|
||||||
- Params: none
|
- Params: none
|
||||||
- Response:
|
- Response:
|
||||||
- On failure: `{"error": "…"}`
|
- On failure: `{"error": "…"}`
|
||||||
- On success: JSON of the `user.info`
|
- On success: JSON of the `user.info`
|
||||||
|
|
||||||
|
## `POST /api/pleroma/admin/users/permission_group/:permission_group`
|
||||||
|
|
||||||
|
### Add users to permission group
|
||||||
|
|
||||||
|
- Params:
|
||||||
|
- `nicknames`: nicknames array
|
||||||
|
- Response:
|
||||||
|
- On failure: `{"error": "…"}`
|
||||||
|
- On success: JSON of the `user.info`
|
||||||
|
|
||||||
|
## DEPRECATED `DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group`
|
||||||
|
|
||||||
### Remove user from permission group
|
### Remove user from permission group
|
||||||
|
|
||||||
- Method: `DELETE`
|
|
||||||
- Params: none
|
- Params: none
|
||||||
- Response:
|
- Response:
|
||||||
- On failure: `{"error": "…"}`
|
- On failure: `{"error": "…"}`
|
||||||
- On success: JSON of the `user.info`
|
- On success: JSON of the `user.info`
|
||||||
- Note: An admin cannot revoke their own admin status.
|
- Note: An admin cannot revoke their own admin status.
|
||||||
|
|
||||||
## `/api/pleroma/admin/users/:nickname/activation_status`
|
## `DELETE /api/pleroma/admin/users/permission_group/:permission_group`
|
||||||
|
|
||||||
|
### Remove users from permission group
|
||||||
|
|
||||||
|
- Params:
|
||||||
|
- `nicknames`: nicknames array
|
||||||
|
- Response:
|
||||||
|
- On failure: `{"error": "…"}`
|
||||||
|
- On success: JSON of the `user.info`
|
||||||
|
- Note: An admin cannot revoke their own admin status.
|
||||||
|
|
||||||
|
## `PATCH /api/pleroma/admin/users/activate`
|
||||||
|
|
||||||
|
### Activate user
|
||||||
|
|
||||||
|
- Params:
|
||||||
|
- `nicknames`: nicknames array
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
users: [
|
||||||
|
{
|
||||||
|
// user object
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `PATCH /api/pleroma/admin/users/deactivate`
|
||||||
|
|
||||||
|
### Deactivate user
|
||||||
|
|
||||||
|
- Params:
|
||||||
|
- `nicknames`: nicknames array
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
users: [
|
||||||
|
{
|
||||||
|
// user object
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## DEPRECATED `PATCH /api/pleroma/admin/users/:nickname/activation_status`
|
||||||
|
|
||||||
### Active or deactivate a user
|
### Active or deactivate a user
|
||||||
|
|
||||||
- Method: `PUT`
|
|
||||||
- Params:
|
- Params:
|
||||||
- `nickname`
|
- `nickname`
|
||||||
- `status` BOOLEAN field, false value means deactivation.
|
- `status` BOOLEAN field, false value means deactivation.
|
||||||
|
|
|
@ -86,18 +86,18 @@ defp parse_datetime(datetime) do
|
||||||
parsed_datetime
|
parsed_datetime
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec insert_log(%{actor: User, subject: User, action: String.t(), permission: String.t()}) ::
|
@spec insert_log(%{actor: User, subject: [User], action: String.t(), permission: String.t()}) ::
|
||||||
{:ok, ModerationLog} | {:error, any}
|
{:ok, ModerationLog} | {:error, any}
|
||||||
def insert_log(%{
|
def insert_log(%{
|
||||||
actor: %User{} = actor,
|
actor: %User{} = actor,
|
||||||
subject: %User{} = subject,
|
subject: subjects,
|
||||||
action: action,
|
action: action,
|
||||||
permission: permission
|
permission: permission
|
||||||
}) do
|
}) do
|
||||||
%ModerationLog{
|
%ModerationLog{
|
||||||
data: %{
|
data: %{
|
||||||
"actor" => user_to_map(actor),
|
"actor" => user_to_map(actor),
|
||||||
"subject" => user_to_map(subject),
|
"subject" => user_to_map(subjects),
|
||||||
"action" => action,
|
"action" => action,
|
||||||
"permission" => permission,
|
"permission" => permission,
|
||||||
"message" => ""
|
"message" => ""
|
||||||
|
@ -303,13 +303,16 @@ def insert_log(%{
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec insert_log_entry_with_message(ModerationLog) :: {:ok, ModerationLog} | {:error, any}
|
@spec insert_log_entry_with_message(ModerationLog) :: {:ok, ModerationLog} | {:error, any}
|
||||||
|
|
||||||
defp insert_log_entry_with_message(entry) do
|
defp insert_log_entry_with_message(entry) do
|
||||||
entry.data["message"]
|
entry.data["message"]
|
||||||
|> put_in(get_log_entry_message(entry))
|
|> put_in(get_log_entry_message(entry))
|
||||||
|> Repo.insert()
|
|> Repo.insert()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp user_to_map(users) when is_list(users) do
|
||||||
|
users |> Enum.map(&user_to_map/1)
|
||||||
|
end
|
||||||
|
|
||||||
defp user_to_map(%User{} = user) do
|
defp user_to_map(%User{} = user) do
|
||||||
user
|
user
|
||||||
|> Map.from_struct()
|
|> Map.from_struct()
|
||||||
|
@ -349,10 +352,10 @@ def get_log_entry_message(%ModerationLog{
|
||||||
data: %{
|
data: %{
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
"action" => "delete",
|
"action" => "delete",
|
||||||
"subject" => %{"nickname" => subject_nickname, "type" => "user"}
|
"subject" => subjects
|
||||||
}
|
}
|
||||||
}) do
|
}) do
|
||||||
"@#{actor_nickname} deleted user @#{subject_nickname}"
|
"@#{actor_nickname} deleted users: #{users_to_nicknames_string(subjects)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
@ -363,12 +366,7 @@ def get_log_entry_message(%ModerationLog{
|
||||||
"subjects" => subjects
|
"subjects" => subjects
|
||||||
}
|
}
|
||||||
}) do
|
}) do
|
||||||
nicknames =
|
"@#{actor_nickname} created users: #{users_to_nicknames_string(subjects)}"
|
||||||
subjects
|
|
||||||
|> Enum.map(&"@#{&1["nickname"]}")
|
|
||||||
|> Enum.join(", ")
|
|
||||||
|
|
||||||
"@#{actor_nickname} created users: #{nicknames}"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
@ -376,10 +374,10 @@ def get_log_entry_message(%ModerationLog{
|
||||||
data: %{
|
data: %{
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
"action" => "activate",
|
"action" => "activate",
|
||||||
"subject" => %{"nickname" => subject_nickname, "type" => "user"}
|
"subject" => users
|
||||||
}
|
}
|
||||||
}) do
|
}) do
|
||||||
"@#{actor_nickname} activated user @#{subject_nickname}"
|
"@#{actor_nickname} activated users: #{users_to_nicknames_string(users)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
@ -387,10 +385,10 @@ def get_log_entry_message(%ModerationLog{
|
||||||
data: %{
|
data: %{
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
"action" => "deactivate",
|
"action" => "deactivate",
|
||||||
"subject" => %{"nickname" => subject_nickname, "type" => "user"}
|
"subject" => users
|
||||||
}
|
}
|
||||||
}) do
|
}) do
|
||||||
"@#{actor_nickname} deactivated user @#{subject_nickname}"
|
"@#{actor_nickname} deactivated users: #{users_to_nicknames_string(users)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
@ -402,14 +400,9 @@ def get_log_entry_message(%ModerationLog{
|
||||||
"action" => "tag"
|
"action" => "tag"
|
||||||
}
|
}
|
||||||
}) do
|
}) do
|
||||||
nicknames_string =
|
|
||||||
nicknames
|
|
||||||
|> Enum.map(&"@#{&1}")
|
|
||||||
|> Enum.join(", ")
|
|
||||||
|
|
||||||
tags_string = tags |> Enum.join(", ")
|
tags_string = tags |> Enum.join(", ")
|
||||||
|
|
||||||
"@#{actor_nickname} added tags: #{tags_string} to users: #{nicknames_string}"
|
"@#{actor_nickname} added tags: #{tags_string} to users: #{nicknames_to_string(nicknames)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
@ -421,14 +414,9 @@ def get_log_entry_message(%ModerationLog{
|
||||||
"action" => "untag"
|
"action" => "untag"
|
||||||
}
|
}
|
||||||
}) do
|
}) do
|
||||||
nicknames_string =
|
|
||||||
nicknames
|
|
||||||
|> Enum.map(&"@#{&1}")
|
|
||||||
|> Enum.join(", ")
|
|
||||||
|
|
||||||
tags_string = tags |> Enum.join(", ")
|
tags_string = tags |> Enum.join(", ")
|
||||||
|
|
||||||
"@#{actor_nickname} removed tags: #{tags_string} from users: #{nicknames_string}"
|
"@#{actor_nickname} removed tags: #{tags_string} from users: #{nicknames_to_string(nicknames)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
@ -436,11 +424,11 @@ def get_log_entry_message(%ModerationLog{
|
||||||
data: %{
|
data: %{
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
"action" => "grant",
|
"action" => "grant",
|
||||||
"subject" => %{"nickname" => subject_nickname},
|
"subject" => users,
|
||||||
"permission" => permission
|
"permission" => permission
|
||||||
}
|
}
|
||||||
}) do
|
}) do
|
||||||
"@#{actor_nickname} made @#{subject_nickname} #{permission}"
|
"@#{actor_nickname} made #{users_to_nicknames_string(users)} #{permission}"
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
@ -448,11 +436,11 @@ def get_log_entry_message(%ModerationLog{
|
||||||
data: %{
|
data: %{
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
"action" => "revoke",
|
"action" => "revoke",
|
||||||
"subject" => %{"nickname" => subject_nickname},
|
"subject" => users,
|
||||||
"permission" => permission
|
"permission" => permission
|
||||||
}
|
}
|
||||||
}) do
|
}) do
|
||||||
"@#{actor_nickname} revoked #{permission} role from @#{subject_nickname}"
|
"@#{actor_nickname} revoked #{permission} role from #{users_to_nicknames_string(users)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
@ -551,4 +539,16 @@ def get_log_entry_message(%ModerationLog{
|
||||||
}) do
|
}) do
|
||||||
"@#{actor_nickname} deleted status ##{subject_id}"
|
"@#{actor_nickname} deleted status ##{subject_id}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp nicknames_to_string(nicknames) do
|
||||||
|
nicknames
|
||||||
|
|> Enum.map(&"@#{&1}")
|
||||||
|
|> Enum.join(", ")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp users_to_nicknames_string(users) do
|
||||||
|
users
|
||||||
|
|> Enum.map(&"@#{&1["nickname"]}")
|
||||||
|
|> Enum.join(", ")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1061,7 +1061,15 @@ def deactivate_async(user, status \\ true) do
|
||||||
BackgroundWorker.enqueue("deactivate_user", %{"user_id" => user.id, "status" => status})
|
BackgroundWorker.enqueue("deactivate_user", %{"user_id" => user.id, "status" => status})
|
||||||
end
|
end
|
||||||
|
|
||||||
def deactivate(%User{} = user, status \\ true) do
|
def deactivate(user, status \\ true)
|
||||||
|
|
||||||
|
def deactivate(users, status) when is_list(users) do
|
||||||
|
Repo.transaction(fn ->
|
||||||
|
for user <- users, do: deactivate(user, status)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def deactivate(%User{} = user, status) do
|
||||||
with {:ok, user} <- update_info(user, &User.Info.set_activation_status(&1, status)) do
|
with {:ok, user} <- update_info(user, &User.Info.set_activation_status(&1, status)) do
|
||||||
Enum.each(get_followers(user), &invalidate_cache/1)
|
Enum.each(get_followers(user), &invalidate_cache/1)
|
||||||
Enum.each(get_friends(user), &update_follower_count/1)
|
Enum.each(get_friends(user), &update_follower_count/1)
|
||||||
|
@ -1074,6 +1082,10 @@ def update_notification_settings(%User{} = user, settings \\ %{}) do
|
||||||
update_info(user, &User.Info.update_notification_settings(&1, settings))
|
update_info(user, &User.Info.update_notification_settings(&1, settings))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def delete(users) when is_list(users) do
|
||||||
|
for user <- users, do: delete(user)
|
||||||
|
end
|
||||||
|
|
||||||
def delete(%User{} = user) do
|
def delete(%User{} = user) do
|
||||||
BackgroundWorker.enqueue("delete_user", %{"user_id" => user.id})
|
BackgroundWorker.enqueue("delete_user", %{"user_id" => user.id})
|
||||||
end
|
end
|
||||||
|
@ -1627,6 +1639,12 @@ def change_info(user, fun) do
|
||||||
|
|
||||||
`fun` is called with the `user.info`.
|
`fun` is called with the `user.info`.
|
||||||
"""
|
"""
|
||||||
|
def update_info(users, fun) when is_list(users) do
|
||||||
|
Repo.transaction(fn ->
|
||||||
|
for user <- users, do: update_info(user, fun)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
def update_info(user, fun) do
|
def update_info(user, fun) do
|
||||||
user
|
user
|
||||||
|> change_info(fun)
|
|> change_info(fun)
|
||||||
|
|
|
@ -46,6 +46,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
:user_delete,
|
:user_delete,
|
||||||
:users_create,
|
:users_create,
|
||||||
:user_toggle_activation,
|
:user_toggle_activation,
|
||||||
|
:user_activate,
|
||||||
|
:user_deactivate,
|
||||||
:tag_users,
|
:tag_users,
|
||||||
:untag_users,
|
:untag_users,
|
||||||
:right_add,
|
:right_add,
|
||||||
|
@ -98,7 +100,7 @@ def user_delete(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
|
||||||
|
|
||||||
ModerationLog.insert_log(%{
|
ModerationLog.insert_log(%{
|
||||||
actor: admin,
|
actor: admin,
|
||||||
subject: user,
|
subject: [user],
|
||||||
action: "delete"
|
action: "delete"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -106,6 +108,20 @@ def user_delete(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
|
||||||
|> json(nickname)
|
|> json(nickname)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def user_delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||||
|
users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
|
||||||
|
User.delete(users)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: users,
|
||||||
|
action: "delete"
|
||||||
|
})
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> json(nicknames)
|
||||||
|
end
|
||||||
|
|
||||||
def user_follow(%{assigns: %{user: admin}} = conn, %{
|
def user_follow(%{assigns: %{user: admin}} = conn, %{
|
||||||
"follower" => follower_nick,
|
"follower" => follower_nick,
|
||||||
"followed" => followed_nick
|
"followed" => followed_nick
|
||||||
|
@ -240,7 +256,7 @@ def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => ni
|
||||||
|
|
||||||
ModerationLog.insert_log(%{
|
ModerationLog.insert_log(%{
|
||||||
actor: admin,
|
actor: admin,
|
||||||
subject: user,
|
subject: [user],
|
||||||
action: action
|
action: action
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -249,6 +265,36 @@ def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => ni
|
||||||
|> render("show.json", %{user: updated_user})
|
|> render("show.json", %{user: updated_user})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def user_activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||||
|
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||||
|
{:ok, updated_users} = User.deactivate(users, false)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: users,
|
||||||
|
action: "activate"
|
||||||
|
})
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_view(AccountView)
|
||||||
|
|> render("index.json", %{users: Keyword.values(updated_users)})
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||||
|
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||||
|
{:ok, updated_users} = User.deactivate(users, true)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: users,
|
||||||
|
action: "deactivate"
|
||||||
|
})
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_view(AccountView)
|
||||||
|
|> render("index.json", %{users: Keyword.values(updated_users)})
|
||||||
|
end
|
||||||
|
|
||||||
def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
|
def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
|
||||||
with {:ok, _} <- User.tag(nicknames, tags) do
|
with {:ok, _} <- User.tag(nicknames, tags) do
|
||||||
ModerationLog.insert_log(%{
|
ModerationLog.insert_log(%{
|
||||||
|
@ -313,6 +359,31 @@ defp maybe_parse_filters(filters) do
|
||||||
|> Enum.into(%{}, &{&1, true})
|
|> Enum.into(%{}, &{&1, true})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def right_add_multiple(%{assigns: %{user: admin}} = conn, %{
|
||||||
|
"permission_group" => permission_group,
|
||||||
|
"nicknames" => nicknames
|
||||||
|
})
|
||||||
|
when permission_group in ["moderator", "admin"] do
|
||||||
|
info = Map.put(%{}, "is_" <> permission_group, true)
|
||||||
|
|
||||||
|
users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
|
||||||
|
|
||||||
|
User.update_info(users, &User.Info.admin_api_update(&1, info))
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "grant",
|
||||||
|
actor: admin,
|
||||||
|
subject: users,
|
||||||
|
permission: permission_group
|
||||||
|
})
|
||||||
|
|
||||||
|
json(conn, info)
|
||||||
|
end
|
||||||
|
|
||||||
|
def right_add_multiple(conn, _) do
|
||||||
|
render_error(conn, :not_found, "No such permission_group")
|
||||||
|
end
|
||||||
|
|
||||||
def right_add(%{assigns: %{user: admin}} = conn, %{
|
def right_add(%{assigns: %{user: admin}} = conn, %{
|
||||||
"permission_group" => permission_group,
|
"permission_group" => permission_group,
|
||||||
"nickname" => nickname
|
"nickname" => nickname
|
||||||
|
@ -328,7 +399,7 @@ def right_add(%{assigns: %{user: admin}} = conn, %{
|
||||||
ModerationLog.insert_log(%{
|
ModerationLog.insert_log(%{
|
||||||
action: "grant",
|
action: "grant",
|
||||||
actor: admin,
|
actor: admin,
|
||||||
subject: user,
|
subject: [user],
|
||||||
permission: permission_group
|
permission: permission_group
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -349,8 +420,36 @@ def right_get(conn, %{"nickname" => nickname}) do
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname" => nickname}) do
|
def right_delete_multiple(
|
||||||
render_error(conn, :forbidden, "You can't revoke your own admin status.")
|
%{assigns: %{user: %{nickname: admin_nickname} = admin}} = conn,
|
||||||
|
%{
|
||||||
|
"permission_group" => permission_group,
|
||||||
|
"nicknames" => nicknames
|
||||||
|
}
|
||||||
|
)
|
||||||
|
when permission_group in ["moderator", "admin"] do
|
||||||
|
with false <- Enum.member?(nicknames, admin_nickname) do
|
||||||
|
info = Map.put(%{}, "is_" <> permission_group, false)
|
||||||
|
|
||||||
|
users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
|
||||||
|
|
||||||
|
User.update_info(users, &User.Info.admin_api_update(&1, info))
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "revoke",
|
||||||
|
actor: admin,
|
||||||
|
subject: users,
|
||||||
|
permission: permission_group
|
||||||
|
})
|
||||||
|
|
||||||
|
json(conn, info)
|
||||||
|
else
|
||||||
|
_ -> render_error(conn, :forbidden, "You can't revoke your own admin/moderator status.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def right_delete_multiple(conn, _) do
|
||||||
|
render_error(conn, :not_found, "No such permission_group")
|
||||||
end
|
end
|
||||||
|
|
||||||
def right_delete(
|
def right_delete(
|
||||||
|
@ -371,34 +470,15 @@ def right_delete(
|
||||||
ModerationLog.insert_log(%{
|
ModerationLog.insert_log(%{
|
||||||
action: "revoke",
|
action: "revoke",
|
||||||
actor: admin,
|
actor: admin,
|
||||||
subject: user,
|
subject: [user],
|
||||||
permission: permission_group
|
permission: permission_group
|
||||||
})
|
})
|
||||||
|
|
||||||
json(conn, info)
|
json(conn, info)
|
||||||
end
|
end
|
||||||
|
|
||||||
def right_delete(conn, _) do
|
def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname" => nickname}) do
|
||||||
render_error(conn, :not_found, "No such permission_group")
|
render_error(conn, :forbidden, "You can't revoke your own admin status.")
|
||||||
end
|
|
||||||
|
|
||||||
def set_activation_status(%{assigns: %{user: admin}} = conn, %{
|
|
||||||
"nickname" => nickname,
|
|
||||||
"status" => status
|
|
||||||
}) do
|
|
||||||
with {:ok, status} <- Ecto.Type.cast(:boolean, status),
|
|
||||||
%User{} = user <- User.get_cached_by_nickname(nickname),
|
|
||||||
{:ok, _} <- User.deactivate(user, !status) do
|
|
||||||
action = if(user.info.deactivated, do: "activate", else: "deactivate")
|
|
||||||
|
|
||||||
ModerationLog.insert_log(%{
|
|
||||||
actor: admin,
|
|
||||||
subject: user,
|
|
||||||
action: action
|
|
||||||
})
|
|
||||||
|
|
||||||
json_response(conn, :no_content, "")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def relay_follow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
|
def relay_follow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
|
||||||
|
|
|
@ -19,6 +19,12 @@ def render("index.json", %{users: users, count: count, page_size: page_size}) do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def render("index.json", %{users: users}) do
|
||||||
|
%{
|
||||||
|
users: render_many(users, AccountView, "show.json", as: :user)
|
||||||
|
}
|
||||||
|
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 = HTML.strip_tags(user.name || user.nickname)
|
display_name = HTML.strip_tags(user.name || user.nickname)
|
||||||
|
|
|
@ -137,11 +137,14 @@ defmodule Pleroma.Web.Router do
|
||||||
delete("/users", AdminAPIController, :user_delete)
|
delete("/users", AdminAPIController, :user_delete)
|
||||||
post("/users", AdminAPIController, :users_create)
|
post("/users", AdminAPIController, :users_create)
|
||||||
patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)
|
patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)
|
||||||
|
patch("/users/activate", AdminAPIController, :user_activate)
|
||||||
|
patch("/users/deactivate", AdminAPIController, :user_deactivate)
|
||||||
put("/users/tag", AdminAPIController, :tag_users)
|
put("/users/tag", AdminAPIController, :tag_users)
|
||||||
delete("/users/tag", AdminAPIController, :untag_users)
|
delete("/users/tag", AdminAPIController, :untag_users)
|
||||||
|
|
||||||
get("/users/:nickname/permission_group", AdminAPIController, :right_get)
|
get("/users/:nickname/permission_group", AdminAPIController, :right_get)
|
||||||
get("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_get)
|
get("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_get)
|
||||||
|
|
||||||
post("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_add)
|
post("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_add)
|
||||||
|
|
||||||
delete(
|
delete(
|
||||||
|
@ -150,7 +153,13 @@ defmodule Pleroma.Web.Router do
|
||||||
:right_delete
|
:right_delete
|
||||||
)
|
)
|
||||||
|
|
||||||
put("/users/:nickname/activation_status", AdminAPIController, :set_activation_status)
|
post("/users/permission_group/:permission_group", AdminAPIController, :right_add_multiple)
|
||||||
|
|
||||||
|
delete(
|
||||||
|
"/users/permission_group/:permission_group",
|
||||||
|
AdminAPIController,
|
||||||
|
:right_delete_multiple
|
||||||
|
)
|
||||||
|
|
||||||
post("/relay", AdminAPIController, :relay_follow)
|
post("/relay", AdminAPIController, :relay_follow)
|
||||||
delete("/relay", AdminAPIController, :relay_unfollow)
|
delete("/relay", AdminAPIController, :relay_unfollow)
|
||||||
|
|
|
@ -24,13 +24,13 @@ test "logging user deletion by moderator", %{moderator: moderator, subject1: sub
|
||||||
{:ok, _} =
|
{:ok, _} =
|
||||||
ModerationLog.insert_log(%{
|
ModerationLog.insert_log(%{
|
||||||
actor: moderator,
|
actor: moderator,
|
||||||
subject: subject1,
|
subject: [subject1],
|
||||||
action: "delete"
|
action: "delete"
|
||||||
})
|
})
|
||||||
|
|
||||||
log = Repo.one(ModerationLog)
|
log = Repo.one(ModerationLog)
|
||||||
|
|
||||||
assert log.data["message"] == "@#{moderator.nickname} deleted user @#{subject1.nickname}"
|
assert log.data["message"] == "@#{moderator.nickname} deleted users: @#{subject1.nickname}"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "logging user creation by moderator", %{
|
test "logging user creation by moderator", %{
|
||||||
|
@ -128,7 +128,7 @@ test "logging user grant by moderator", %{moderator: moderator, subject1: subjec
|
||||||
{:ok, _} =
|
{:ok, _} =
|
||||||
ModerationLog.insert_log(%{
|
ModerationLog.insert_log(%{
|
||||||
actor: moderator,
|
actor: moderator,
|
||||||
subject: subject1,
|
subject: [subject1],
|
||||||
action: "grant",
|
action: "grant",
|
||||||
permission: "moderator"
|
permission: "moderator"
|
||||||
})
|
})
|
||||||
|
@ -142,7 +142,7 @@ test "logging user revoke by moderator", %{moderator: moderator, subject1: subje
|
||||||
{:ok, _} =
|
{:ok, _} =
|
||||||
ModerationLog.insert_log(%{
|
ModerationLog.insert_log(%{
|
||||||
actor: moderator,
|
actor: moderator,
|
||||||
subject: subject1,
|
subject: [subject1],
|
||||||
action: "revoke",
|
action: "revoke",
|
||||||
permission: "moderator"
|
permission: "moderator"
|
||||||
})
|
})
|
||||||
|
|
|
@ -17,8 +17,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
describe "/api/pleroma/admin/users" do
|
describe "DELETE /api/pleroma/admin/users" do
|
||||||
test "Delete" do
|
test "single user" do
|
||||||
admin = insert(:user, info: %{is_admin: true})
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
@ -30,15 +30,36 @@ test "Delete" do
|
||||||
|
|
||||||
log_entry = Repo.one(ModerationLog)
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
assert log_entry.data["subject"]["nickname"] == user.nickname
|
|
||||||
assert log_entry.data["action"] == "delete"
|
|
||||||
|
|
||||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
"@#{admin.nickname} deleted user @#{user.nickname}"
|
"@#{admin.nickname} deleted users: @#{user.nickname}"
|
||||||
|
|
||||||
assert json_response(conn, 200) == user.nickname
|
assert json_response(conn, 200) == user.nickname
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "multiple users" do
|
||||||
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
|
user_one = insert(:user)
|
||||||
|
user_two = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> delete("/api/pleroma/admin/users", %{
|
||||||
|
nicknames: [user_one.nickname, user_two.nickname]
|
||||||
|
})
|
||||||
|
|
||||||
|
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" do
|
test "Create" do
|
||||||
admin = insert(:user, info: %{is_admin: true})
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
|
|
||||||
|
@ -404,6 +425,29 @@ test "/:right POST, can add to a permission group" do
|
||||||
"@#{admin.nickname} made @#{user.nickname} admin"
|
"@#{admin.nickname} made @#{user.nickname} admin"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "/:right POST, can add to a permission group (multiple)" do
|
||||||
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
|
user_one = insert(:user)
|
||||||
|
user_two = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users/permission_group/admin", %{
|
||||||
|
nicknames: [user_one.nickname, user_two.nickname]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{
|
||||||
|
"is_admin" => true
|
||||||
|
}
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin"
|
||||||
|
end
|
||||||
|
|
||||||
test "/:right DELETE, can remove from a permission group" do
|
test "/:right DELETE, can remove from a permission group" do
|
||||||
admin = insert(:user, info: %{is_admin: true})
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
user = insert(:user, info: %{is_admin: true})
|
user = insert(:user, info: %{is_admin: true})
|
||||||
|
@ -423,63 +467,30 @@ test "/:right DELETE, can remove from a permission group" do
|
||||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
"@#{admin.nickname} revoked admin role from @#{user.nickname}"
|
"@#{admin.nickname} revoked admin role from @#{user.nickname}"
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
describe "PUT /api/pleroma/admin/users/:nickname/activation_status" do
|
test "/:right DELETE, can remove from a permission group (multiple)" do
|
||||||
setup %{conn: conn} do
|
|
||||||
admin = insert(:user, info: %{is_admin: true})
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
|
user_one = insert(:user, info: %{is_admin: true})
|
||||||
|
user_two = insert(:user, info: %{is_admin: true})
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
conn
|
build_conn()
|
||||||
|> assign(:user, admin)
|
|> assign(:user, admin)
|
||||||
|> put_req_header("accept", "application/json")
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> delete("/api/pleroma/admin/users/permission_group/admin", %{
|
||||||
|
nicknames: [user_one.nickname, user_two.nickname]
|
||||||
|
})
|
||||||
|
|
||||||
%{conn: conn, admin: admin}
|
assert json_response(conn, 200) == %{
|
||||||
end
|
"is_admin" => false
|
||||||
|
}
|
||||||
test "deactivates the user", %{conn: conn, admin: admin} do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> put("/api/pleroma/admin/users/#{user.nickname}/activation_status", %{status: false})
|
|
||||||
|
|
||||||
user = User.get_cached_by_id(user.id)
|
|
||||||
assert user.info.deactivated == true
|
|
||||||
assert json_response(conn, :no_content)
|
|
||||||
|
|
||||||
log_entry = Repo.one(ModerationLog)
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
"@#{admin.nickname} deactivated user @#{user.nickname}"
|
"@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{
|
||||||
end
|
user_two.nickname
|
||||||
|
}"
|
||||||
test "activates the user", %{conn: conn, admin: admin} do
|
|
||||||
user = insert(:user, info: %{deactivated: true})
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> put("/api/pleroma/admin/users/#{user.nickname}/activation_status", %{status: true})
|
|
||||||
|
|
||||||
user = User.get_cached_by_id(user.id)
|
|
||||||
assert user.info.deactivated == false
|
|
||||||
assert json_response(conn, :no_content)
|
|
||||||
|
|
||||||
log_entry = Repo.one(ModerationLog)
|
|
||||||
|
|
||||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
|
||||||
"@#{admin.nickname} activated user @#{user.nickname}"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns 403 when requested by a non-admin", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> put("/api/pleroma/admin/users/#{user.nickname}/activation_status", %{status: false})
|
|
||||||
|
|
||||||
assert json_response(conn, :forbidden)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1029,6 +1040,50 @@ test "it works with multiple filters" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "PATCH /api/pleroma/admin/users/activate" do
|
||||||
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
|
user_one = insert(:user, info: %{deactivated: true})
|
||||||
|
user_two = insert(:user, info: %{deactivated: true})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> patch(
|
||||||
|
"/api/pleroma/admin/users/activate",
|
||||||
|
%{nicknames: [user_one.nickname, user_two.nickname]}
|
||||||
|
)
|
||||||
|
|
||||||
|
response = json_response(conn, 200)
|
||||||
|
assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "PATCH /api/pleroma/admin/users/deactivate" do
|
||||||
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
|
user_one = insert(:user, info: %{deactivated: false})
|
||||||
|
user_two = insert(:user, info: %{deactivated: false})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> patch(
|
||||||
|
"/api/pleroma/admin/users/deactivate",
|
||||||
|
%{nicknames: [user_one.nickname, user_two.nickname]}
|
||||||
|
)
|
||||||
|
|
||||||
|
response = json_response(conn, 200)
|
||||||
|
assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
|
||||||
|
end
|
||||||
|
|
||||||
test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation" do
|
test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation" do
|
||||||
admin = insert(:user, info: %{is_admin: true})
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
@ -1053,7 +1108,7 @@ test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation" do
|
||||||
log_entry = Repo.one(ModerationLog)
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
"@#{admin.nickname} deactivated user @#{user.nickname}"
|
"@#{admin.nickname} deactivated users: @#{user.nickname}"
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "POST /api/pleroma/admin/users/invite_token" do
|
describe "POST /api/pleroma/admin/users/invite_token" do
|
||||||
|
|
Loading…
Reference in a new issue