Merge branch 'fine_grained_moderation_privileges' into 'develop'

fine grained moderation privileges (continued)

See merge request pleroma/pleroma!3812
This commit is contained in:
Haelwenn 2022-12-23 13:48:07 +00:00
commit 718ff64c3b
42 changed files with 1816 additions and 443 deletions

View file

@ -24,8 +24,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- `activeMonth` and `activeHalfyear` fields in NodeInfo usage.users object - `activeMonth` and `activeHalfyear` fields in NodeInfo usage.users object
- Experimental support for Finch. Put `config :tesla, :adapter, {Tesla.Adapter.Finch, name: MyFinch}` in your secrets file to use it. Reverse Proxy will still use Hackney. - Experimental support for Finch. Put `config :tesla, :adapter, {Tesla.Adapter.Finch, name: MyFinch}` in your secrets file to use it. Reverse Proxy will still use Hackney.
- `ForceMentionsInPostContent` MRF policy - `ForceMentionsInPostContent` MRF policy
- AdminAPI: allow moderators to manage reports, users, invites, and custom emojis
- AdminAPI: restrict moderators to access sensitive data: change user credentials, get password reset token, read private statuses and chats, etc
- PleromaAPI: Add remote follow API endpoint at `POST /api/v1/pleroma/remote_interaction` - PleromaAPI: Add remote follow API endpoint at `POST /api/v1/pleroma/remote_interaction`
- MastoAPI: Add `GET /api/v1/accounts/lookup` - MastoAPI: Add `GET /api/v1/accounts/lookup`
- MastoAPI: Profile Directory support - MastoAPI: Profile Directory support
@ -37,6 +35,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Configuration: Add `birthday_required` and `birthday_min_age` settings to provide a way to require users to enter their birth date. - Configuration: Add `birthday_required` and `birthday_min_age` settings to provide a way to require users to enter their birth date.
- PleromaAPI: Add `GET /api/v1/pleroma/birthdays` API endpoint - PleromaAPI: Add `GET /api/v1/pleroma/birthdays` API endpoint
- Make backend-rendered pages translatable. This includes emails. Pages returned as a HTTP response are translated using the language specified in the `userLanguage` cookie, or the `Accept-Language` header. Emails are translated using the `language` field when registering. This language can be changed by `PATCH /api/v1/accounts/update_credentials` with the `language` field. - Make backend-rendered pages translatable. This includes emails. Pages returned as a HTTP response are translated using the language specified in the `userLanguage` cookie, or the `Accept-Language` header. Emails are translated using the `language` field when registering. This language can be changed by `PATCH /api/v1/accounts/update_credentials` with the `language` field.
- Add fine grained options to provide privileges to moderators and admins (e.g. delete messages, manage reports...)
- Uploadfilter `Pleroma.Upload.Filter.Exiftool.ReadDescription` returns description values to the FE so they can pre fill the image description field - Uploadfilter `Pleroma.Upload.Filter.Exiftool.ReadDescription` returns description values to the FE so they can pre fill the image description field
- Added move account API - Added move account API
- Enable remote users to interact with posts - Enable remote users to interact with posts

View file

@ -253,7 +253,23 @@ config :pleroma, :instance,
show_reactions: true, show_reactions: true,
password_reset_token_validity: 60 * 60 * 24, password_reset_token_validity: 60 * 60 * 24,
profile_directory: true, profile_directory: true,
privileged_staff: false, admin_privileges: [
:users_read,
:users_manage_invites,
:users_manage_activation_state,
:users_manage_tags,
:users_manage_credentials,
:users_delete,
:messages_read,
:messages_delete,
:instances_delete,
:reports_manage_reports,
:moderation_log_read,
:announcements_manage_announcements,
:emoji_manage_emoji,
:statistics_read
],
moderator_privileges: [:messages_delete, :reports_manage_reports],
max_endorsed_users: 20, max_endorsed_users: 20,
birthday_required: false, birthday_required: false,
birthday_min_age: 0, birthday_min_age: 0,

View file

@ -998,10 +998,48 @@ config :pleroma, :config_description, [
description: "Enable profile directory." description: "Enable profile directory."
}, },
%{ %{
key: :privileged_staff, key: :admin_privileges,
type: :boolean, type: {:list, :atom},
suggestions: [
:users_read,
:users_manage_invites,
:users_manage_activation_state,
:users_manage_tags,
:users_manage_credentials,
:users_delete,
:messages_read,
:messages_delete,
:instances_delete,
:reports_manage_reports,
:moderation_log_read,
:announcements_manage_announcements,
:emoji_manage_emoji,
:statistics_read
],
description: description:
"Let moderators access sensitive data (e.g. updating user credentials, get password reset token, delete users, index and read private statuses and chats)" "What extra privileges to allow admins (e.g. updating user credentials, get password reset token, delete users, index and read private statuses and chats)"
},
%{
key: :moderator_privileges,
type: {:list, :atom},
suggestions: [
:users_read,
:users_manage_invites,
:users_manage_activation_state,
:users_manage_tags,
:users_manage_credentials,
:users_delete,
:messages_read,
:messages_delete,
:instances_delete,
:reports_manage_reports,
:moderation_log_read,
:announcements_manage_announcements,
:emoji_manage_emoji,
:statistics_read
],
description:
"What extra privileges to allow moderators (e.g. updating user credentials, get password reset token, delete users, index and read private statuses and chats)"
}, },
%{ %{
key: :birthday_required, key: :birthday_required,

View file

@ -66,6 +66,36 @@ To add configuration to your config file, you can copy it from the base config.
* `cleanup_attachments`: Remove attachments along with statuses. Does not affect duplicate files and attachments without status. Enabling this will increase load to database when deleting statuses on larger instances. * `cleanup_attachments`: Remove attachments along with statuses. Does not affect duplicate files and attachments without status. Enabling this will increase load to database when deleting statuses on larger instances.
* `show_reactions`: Let favourites and emoji reactions be viewed through the API (default: `true`). * `show_reactions`: Let favourites and emoji reactions be viewed through the API (default: `true`).
* `password_reset_token_validity`: The time after which reset tokens aren't accepted anymore, in seconds (default: one day). * `password_reset_token_validity`: The time after which reset tokens aren't accepted anymore, in seconds (default: one day).
* `admin_privileges`: A list of privileges an admin has (e.g. delete messages, manage reports...)
* Possible values are:
* `:users_read`
* Allows admins to fetch users through the admin API.
* `:users_manage_invites`
* Allows admins to manage invites. This includes sending, resending, revoking and approving invites.
* `:users_manage_activation_state`
* Allows admins to activate and deactivate accounts. This also allows them to see deactivated users through the Mastodon API.
* `:users_manage_tags`
* Allows admins to set and remove tags for users. This can be useful in combination with MRF policies, such as `Pleroma.Web.ActivityPub.MRF.TagPolicy`.
* `:users_manage_credentials`
* Allows admins to trigger a password reset and set new credentials for an user.
* `:users_delete`
* Allows admins to delete accounts. Note that deleting an account is actually deactivating it and removing all data like posts, profile information, etc.
* `:messages_read`
* Allows admins to read messages through the admin API, including non-public posts and chats.
* `:messages_delete`
* Allows admins to delete messages from other users.
* `:instances_delete,`
* Allows admins to remove a whole remote instance from your instance. This will delete all users and messages from that remote instance.
* `:reports_manage_reports`
* Allows admins to see and manage reports.
* `:moderation_log_read,`
* Allows admins to read the entries in the moderation log.
* `:emoji_manage_emoji`
* Allows admins to manage custom emoji on the instance.
* `:statistics_read,`
* Allows admins to see some simple statistics about the instance.
* `moderator_privileges`: A list of privileges a moderator has (e.g. delete messages, manage reports...)
* Possible values are the same as for `admin_privileges`
## :database ## :database
* `improved_hashtag_timeline`: Setting to force toggle / force disable improved hashtags timeline. `:enabled` forces hashtags to be fetched from `hashtags` table for hashtags timeline. `:disabled` forces object-embedded hashtags to be used (slower). Keep it `:auto` for automatic behaviour (it is auto-set to `:enabled` [unless overridden] when HashtagsTableMigrator completes). * `improved_hashtag_timeline`: Setting to force toggle / force disable improved hashtags timeline. `:enabled` forces hashtags to be fetched from `hashtags` table for hashtags timeline. `:disabled` forces object-embedded hashtags to be used (slower). Keep it `:auto` for automatic behaviour (it is auto-set to `:enabled` [unless overridden] when HashtagsTableMigrator completes).

View file

@ -5,7 +5,7 @@
In this guide we cover how you can migrate from a from source installation to one using OTP releases. In this guide we cover how you can migrate from a from source installation to one using OTP releases.
## Pre-requisites ## Pre-requisites
You will be running commands as root. If you aren't root already, please elevate your priviledges by executing `sudo su`/`su`. You will be running commands as root. If you aren't root already, please elevate your privileges by executing `sudo su`/`su`.
The system needs to have `curl` and `unzip` installed for downloading and unpacking release builds. The system needs to have `curl` and `unzip` installed for downloading and unpacking release builds.

View file

@ -8,7 +8,7 @@ This guide covers a installation using an OTP release. To install Pleroma from s
* A machine running Linux with GNU (e.g. Debian, Ubuntu) or musl (e.g. Alpine) libc and `x86_64`, `aarch64` or `armv7l` CPU, you have root access to. If you are not sure if it's compatible see [Detecting flavour section](#detecting-flavour) below * A machine running Linux with GNU (e.g. Debian, Ubuntu) or musl (e.g. Alpine) libc and `x86_64`, `aarch64` or `armv7l` CPU, you have root access to. If you are not sure if it's compatible see [Detecting flavour section](#detecting-flavour) below
* A (sub)domain pointed to the machine * A (sub)domain pointed to the machine
You will be running commands as root. If you aren't root already, please elevate your priviledges by executing `sudo su`/`su`. You will be running commands as root. If you aren't root already, please elevate your privileges by executing `sudo su`/`su`.
While in theory OTP releases are possbile to install on any compatible machine, for the sake of simplicity this guide focuses only on Debian/Ubuntu and Alpine. While in theory OTP releases are possbile to install on any compatible machine, for the sake of simplicity this guide focuses only on Debian/Ubuntu and Alpine.

View file

@ -338,14 +338,6 @@ defmodule Pleroma.Notification do
|> Repo.delete_all() |> Repo.delete_all()
end end
def destroy_multiple_from_types(%{id: user_id}, types) do
from(n in Notification,
where: n.user_id == ^user_id,
where: n.type in ^types
)
|> Repo.delete_all()
end
def dismiss(%Pleroma.Activity{} = activity) do def dismiss(%Pleroma.Activity{} = activity) do
Notification Notification
|> where([n], n.activity_id == ^activity.id) |> where([n], n.activity_id == ^activity.id)
@ -559,7 +551,9 @@ defmodule Pleroma.Notification do
end end
def get_potential_receiver_ap_ids(%{data: %{"type" => "Flag", "actor" => actor}}) do def get_potential_receiver_ap_ids(%{data: %{"type" => "Flag", "actor" => actor}}) do
(User.all_superusers() |> Enum.map(fn user -> user.ap_id end)) -- [actor] (User.all_users_with_privilege(:reports_manage_reports)
|> Enum.map(fn user -> user.ap_id end)) --
[actor]
end end
# Update activity: notify all who repeated this # Update activity: notify all who repeated this

View file

@ -326,7 +326,7 @@ defmodule Pleroma.User do
end end
def visible_for(%User{} = user, for_user) do def visible_for(%User{} = user, for_user) do
if superuser?(for_user) do if privileged?(for_user, :users_manage_activation_state) do
:visible :visible
else else
visible_account_status(user) visible_account_status(user)
@ -353,10 +353,45 @@ defmodule Pleroma.User do
end end
end end
@spec superuser?(User.t()) :: boolean() @spec privileged?(User.t(), atom()) :: boolean()
def superuser?(%User{local: true, is_admin: true}), do: true def privileged?(%User{is_admin: false, is_moderator: false}, _), do: false
def superuser?(%User{local: true, is_moderator: true}), do: true
def superuser?(_), do: false def privileged?(
%User{local: true, is_admin: is_admin, is_moderator: is_moderator},
privilege_tag
),
do:
privileged_for?(privilege_tag, is_admin, :admin_privileges) or
privileged_for?(privilege_tag, is_moderator, :moderator_privileges)
def privileged?(_, _), do: false
defp privileged_for?(privilege_tag, true, config_role_key),
do: privilege_tag in Config.get([:instance, config_role_key])
defp privileged_for?(_, _, _), do: false
@spec privileges(User.t()) :: [atom()]
def privileges(%User{local: false}) do
[]
end
def privileges(%User{is_moderator: false, is_admin: false}) do
[]
end
def privileges(%User{local: true, is_moderator: true, is_admin: true}) do
(Config.get([:instance, :moderator_privileges]) ++ Config.get([:instance, :admin_privileges]))
|> Enum.uniq()
end
def privileges(%User{local: true, is_moderator: true, is_admin: false}) do
Config.get([:instance, :moderator_privileges])
end
def privileges(%User{local: true, is_moderator: false, is_admin: true}) do
Config.get([:instance, :admin_privileges])
end
@spec invisible?(User.t()) :: boolean() @spec invisible?(User.t()) :: boolean()
def invisible?(%User{invisible: true}), do: true def invisible?(%User{invisible: true}), do: true
@ -1167,24 +1202,10 @@ defmodule Pleroma.User do
|> update_and_set_cache() |> update_and_set_cache()
end end
def update_and_set_cache(%{data: %Pleroma.User{} = user} = changeset) do def update_and_set_cache(changeset) do
was_superuser_before_update = User.superuser?(user)
with {:ok, user} <- Repo.update(changeset, stale_error_field: :id) do with {:ok, user} <- Repo.update(changeset, stale_error_field: :id) do
set_cache(user) set_cache(user)
end end
|> maybe_remove_report_notifications(was_superuser_before_update)
end
defp maybe_remove_report_notifications({:ok, %Pleroma.User{} = user} = result, true) do
if not User.superuser?(user),
do: user |> Notification.destroy_multiple_from_types(["pleroma:report"])
result
end
defp maybe_remove_report_notifications(result, _) do
result
end end
def get_user_friends_ap_ids(user) do def get_user_friends_ap_ids(user) do
@ -2265,6 +2286,11 @@ defmodule Pleroma.User do
|> Repo.all() |> Repo.all()
end end
@spec all_users_with_privilege(atom()) :: [User.t()]
def all_users_with_privilege(privilege) do
User.Query.build(%{is_privileged: privilege}) |> Repo.all()
end
def muting_reblogs?(%User{} = user, %User{} = target) do def muting_reblogs?(%User{} = user, %User{} = target) do
UserRelationship.reblog_mute_exists?(user, target) UserRelationship.reblog_mute_exists?(user, target)
end end

View file

@ -29,6 +29,7 @@ defmodule Pleroma.User.Query do
import Ecto.Query import Ecto.Query
import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1] import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1]
alias Pleroma.Config
alias Pleroma.FollowingRelationship alias Pleroma.FollowingRelationship
alias Pleroma.User alias Pleroma.User
@ -49,6 +50,7 @@ defmodule Pleroma.User.Query do
is_suggested: boolean(), is_suggested: boolean(),
is_discoverable: boolean(), is_discoverable: boolean(),
super_users: boolean(), super_users: boolean(),
is_privileged: atom(),
invisible: boolean(), invisible: boolean(),
internal: boolean(), internal: boolean(),
followers: User.t(), followers: User.t(),
@ -136,6 +138,43 @@ defmodule Pleroma.User.Query do
) )
end end
defp compose_query({:is_privileged, privilege}, query) do
moderator_privileged = privilege in Config.get([:instance, :moderator_privileges])
admin_privileged = privilege in Config.get([:instance, :admin_privileges])
query = compose_query({:active, true}, query)
query = compose_query({:local, true}, query)
case {admin_privileged, moderator_privileged} do
{false, false} ->
where(
query,
false
)
{true, true} ->
where(
query,
[u],
u.is_admin or u.is_moderator
)
{true, false} ->
where(
query,
[u],
u.is_admin
)
{false, true} ->
where(
query,
[u],
u.is_moderator
)
end
end
defp compose_query({:local, _}, query), do: location_query(query, true) defp compose_query({:local, _}, query), do: location_query(query, true)
defp compose_query({:external, _}, query), do: location_query(query, false) defp compose_query({:external, _}, query), do: location_query(query, false)

View file

@ -401,11 +401,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
_ <- notify_and_stream(activity), _ <- notify_and_stream(activity),
:ok <- :ok <-
maybe_federate(stripped_activity) do maybe_federate(stripped_activity) do
User.all_superusers() User.all_users_with_privilege(:reports_manage_reports)
|> Enum.filter(fn user -> user.ap_id != actor end) |> Enum.filter(fn user -> user.ap_id != actor end)
|> Enum.filter(fn user -> not is_nil(user.email) end) |> Enum.filter(fn user -> not is_nil(user.email) end)
|> Enum.each(fn superuser -> |> Enum.each(fn privileged_user ->
superuser privileged_user
|> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content) |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
|> Pleroma.Emails.Mailer.deliver_async() |> Pleroma.Emails.Mailer.deliver_async()
end) end)

View file

@ -136,11 +136,11 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations do
# This figures out if a user is able to create, delete or modify something # This figures out if a user is able to create, delete or modify something
# based on the domain and superuser status # based on the domain and superuser status
@spec validate_modification_rights(Ecto.Changeset.t()) :: Ecto.Changeset.t() @spec validate_modification_rights(Ecto.Changeset.t(), atom()) :: Ecto.Changeset.t()
def validate_modification_rights(cng) do def validate_modification_rights(cng, privilege) do
actor = User.get_cached_by_ap_id(get_field(cng, :actor)) actor = User.get_cached_by_ap_id(get_field(cng, :actor))
if User.superuser?(actor) || same_domain?(cng) do if User.privileged?(actor, privilege) || same_domain?(cng) do
cng cng
else else
cng cng

View file

@ -61,7 +61,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator do
|> validate_required([:id, :type, :actor, :to, :cc, :object]) |> validate_required([:id, :type, :actor, :to, :cc, :object])
|> validate_inclusion(:type, ["Delete"]) |> validate_inclusion(:type, ["Delete"])
|> validate_delete_actor(:actor) |> validate_delete_actor(:actor)
|> validate_modification_rights() |> validate_modification_rights(:messages_delete)
|> validate_object_or_user_presence(allowed_types: @deletable_types) |> validate_object_or_user_presence(allowed_types: @deletable_types)
|> add_deleted_activity_id() |> add_deleted_activity_id()
end end

View file

@ -145,10 +145,10 @@ defmodule Pleroma.Web.CommonAPI do
{:find_activity, Activity.get_by_id(activity_id)}, {:find_activity, Activity.get_by_id(activity_id)},
{_, %Object{} = object, _} <- {_, %Object{} = object, _} <-
{:find_object, Object.normalize(activity, fetch: false), activity}, {:find_object, Object.normalize(activity, fetch: false), activity},
true <- User.superuser?(user) || user.ap_id == object.data["actor"], true <- User.privileged?(user, :messages_delete) || user.ap_id == object.data["actor"],
{:ok, delete_data, _} <- Builder.delete(user, object.data["id"]), {:ok, delete_data, _} <- Builder.delete(user, object.data["id"]),
{:ok, delete, _} <- Pipeline.common_pipeline(delete_data, local: true) do {:ok, delete, _} <- Pipeline.common_pipeline(delete_data, local: true) do
if User.superuser?(user) and user.ap_id != object.data["actor"] do if User.privileged?(user, :messages_delete) and user.ap_id != object.data["actor"] do
action = action =
if object.data["type"] == "ChatMessage" do if object.data["type"] == "ChatMessage" do
"chat_message_delete" "chat_message_delete"

View file

@ -61,7 +61,20 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
end end
def get_notifications(user, params \\ %{}) do def get_notifications(user, params \\ %{}) do
options = cast_params(params) options =
cast_params(params) |> Map.update(:include_types, [], fn include_types -> include_types end)
options =
if ("pleroma:report" not in options.include_types and
User.privileged?(user, :reports_manage_reports)) or
User.privileged?(user, :reports_manage_reports) do
options
else
options
|> Map.update(:exclude_types, ["pleroma:report"], fn current_exclude_types ->
current_exclude_types ++ ["pleroma:report"]
end)
end
user user
|> Notification.for_user_query(options) |> Notification.for_user_query(options)

View file

@ -370,19 +370,22 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
defp maybe_put_chat_token(data, _, _, _), do: data defp maybe_put_chat_token(data, _, _, _), do: data
defp maybe_put_role(data, %User{show_role: true} = user, _) do defp maybe_put_role(data, %User{show_role: true} = user, _) do
data put_role(data, user)
|> Kernel.put_in([:pleroma, :is_admin], user.is_admin)
|> Kernel.put_in([:pleroma, :is_moderator], user.is_moderator)
end end
defp maybe_put_role(data, %User{id: user_id} = user, %User{id: user_id}) do defp maybe_put_role(data, %User{id: user_id} = user, %User{id: user_id}) do
data put_role(data, user)
|> Kernel.put_in([:pleroma, :is_admin], user.is_admin)
|> Kernel.put_in([:pleroma, :is_moderator], user.is_moderator)
end end
defp maybe_put_role(data, _, _), do: data defp maybe_put_role(data, _, _), do: data
defp put_role(data, user) do
data
|> Kernel.put_in([:pleroma, :is_admin], user.is_admin)
|> Kernel.put_in([:pleroma, :is_moderator], user.is_moderator)
|> Kernel.put_in([:pleroma, :privileges], User.privileges(user))
end
defp maybe_put_notification_settings(data, %User{id: user_id} = user, %User{id: user_id}) do defp maybe_put_notification_settings(data, %User{id: user_id} = user, %User{id: user_id}) do
Kernel.put_in( Kernel.put_in(
data, data,
@ -399,12 +402,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
defp maybe_put_allow_following_move(data, _, _), do: data defp maybe_put_allow_following_move(data, _, _), do: data
defp maybe_put_activation_status(data, user, %User{is_admin: true}) do defp maybe_put_activation_status(data, user, user_for) do
Kernel.put_in(data, [:pleroma, :deactivated], !user.is_active) if User.privileged?(user_for, :users_manage_activation_state),
do: Kernel.put_in(data, [:pleroma, :deactivated], !user.is_active),
else: data
end end
defp maybe_put_activation_status(data, _, _), do: data
defp maybe_put_unread_conversation_count(data, %User{id: user_id} = user, %User{id: user_id}) do defp maybe_put_unread_conversation_count(data, %User{id: user_id} = user, %User{id: user_id}) do
data data
|> Kernel.put_in( |> Kernel.put_in(

View file

@ -48,7 +48,6 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
federation: federation(), federation: federation(),
fields_limits: fields_limits(), fields_limits: fields_limits(),
post_formats: Config.get([:instance, :allowed_post_formats]), post_formats: Config.get([:instance, :allowed_post_formats]),
privileged_staff: Config.get([:instance, :privileged_staff]),
birthday_required: Config.get([:instance, :birthday_required]), birthday_required: Config.get([:instance, :birthday_required]),
birthday_min_age: Config.get([:instance, :birthday_min_age]) birthday_min_age: Config.get([:instance, :birthday_min_age])
}, },

View file

@ -49,6 +49,10 @@ defmodule Pleroma.Web.Nodeinfo.Nodeinfo do
enabled: false enabled: false
}, },
staffAccounts: staff_accounts, staffAccounts: staff_accounts,
roles: %{
admin: Config.get([:instance, :admin_privileges]),
moderator: Config.get([:instance, :moderator_privileges])
},
federation: federation, federation: federation,
pollLimits: Config.get([:instance, :poll_limits]), pollLimits: Config.get([:instance, :poll_limits]),
postFormats: Config.get([:instance, :allowed_post_formats]), postFormats: Config.get([:instance, :allowed_post_formats]),
@ -69,8 +73,7 @@ defmodule Pleroma.Web.Nodeinfo.Nodeinfo do
mailerEnabled: Config.get([Pleroma.Emails.Mailer, :enabled], false), mailerEnabled: Config.get([Pleroma.Emails.Mailer, :enabled], false),
features: features, features: features,
restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]), restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]),
skipThreadContainment: Config.get([:instance, :skip_thread_containment], false), skipThreadContainment: Config.get([:instance, :skip_thread_containment], false)
privilegedStaff: Config.get([:instance, :privileged_staff])
} }
} }
end end

View file

@ -0,0 +1,44 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.EnsurePrivilegedPlug do
@moduledoc """
Ensures staff are privileged enough to do certain tasks.
"""
import Pleroma.Web.TranslationHelpers
import Plug.Conn
alias Pleroma.Config
alias Pleroma.User
def init(options) do
options
end
def call(%{assigns: %{user: %User{is_admin: false, is_moderator: false}}} = conn, _) do
conn
|> render_error(:forbidden, "User isn't privileged.")
|> halt()
end
def call(
%{assigns: %{user: %User{is_admin: is_admin, is_moderator: is_moderator}}} = conn,
privilege
) do
if (is_admin and privilege in Config.get([:instance, :admin_privileges])) or
(is_moderator and privilege in Config.get([:instance, :moderator_privileges])) do
conn
else
conn
|> render_error(:forbidden, "User isn't privileged.")
|> halt()
end
end
def call(conn, _) do
conn
|> render_error(:forbidden, "User isn't privileged.")
|> halt()
end
end

View file

@ -1,36 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.EnsureStaffPrivilegedPlug do
@moduledoc """
Ensures staff are privileged enough to do certain tasks.
"""
import Pleroma.Web.TranslationHelpers
import Plug.Conn
alias Pleroma.Config
alias Pleroma.User
def init(options) do
options
end
def call(%{assigns: %{user: %User{is_admin: true}}} = conn, _), do: conn
def call(%{assigns: %{user: %User{is_moderator: true}}} = conn, _) do
if Config.get!([:instance, :privileged_staff]) do
conn
else
conn
|> render_error(:forbidden, "User is not an admin.")
|> halt()
end
end
def call(conn, _) do
conn
|> render_error(:forbidden, "User is not a staff member.")
|> halt()
end
end

View file

@ -101,14 +101,80 @@ defmodule Pleroma.Web.Router do
plug(Pleroma.Web.Plugs.IdempotencyPlug) plug(Pleroma.Web.Plugs.IdempotencyPlug)
end end
pipeline :require_privileged_staff do
plug(Pleroma.Web.Plugs.EnsureStaffPrivilegedPlug)
end
pipeline :require_admin do pipeline :require_admin do
plug(Pleroma.Web.Plugs.UserIsAdminPlug) plug(Pleroma.Web.Plugs.UserIsAdminPlug)
end end
pipeline :require_privileged_role_users_delete do
plug(:admin_api)
plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :users_delete)
end
pipeline :require_privileged_role_users_manage_credentials do
plug(:admin_api)
plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :users_manage_credentials)
end
pipeline :require_privileged_role_messages_read do
plug(:admin_api)
plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :messages_read)
end
pipeline :require_privileged_role_users_manage_tags do
plug(:admin_api)
plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :users_manage_tags)
end
pipeline :require_privileged_role_users_manage_activation_state do
plug(:admin_api)
plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :users_manage_activation_state)
end
pipeline :require_privileged_role_users_manage_invites do
plug(:admin_api)
plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :users_manage_invites)
end
pipeline :require_privileged_role_reports_manage_reports do
plug(:admin_api)
plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :reports_manage_reports)
end
pipeline :require_privileged_role_users_read do
plug(:admin_api)
plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :users_read)
end
pipeline :require_privileged_role_messages_delete do
plug(:admin_api)
plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :messages_delete)
end
pipeline :require_privileged_role_emoji_manage_emoji do
plug(:admin_api)
plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :emoji_manage_emoji)
end
pipeline :require_privileged_role_instances_delete do
plug(:admin_api)
plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :instances_delete)
end
pipeline :require_privileged_role_moderation_log_read do
plug(:admin_api)
plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :moderation_log_read)
end
pipeline :require_privileged_role_statistics_read do
plug(:admin_api)
plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :statistics_read)
end
pipeline :require_privileged_role_announcements_manage_announcements do
plug(:admin_api)
plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :announcements_manage_announcements)
end
pipeline :pleroma_html do pipeline :pleroma_html do
plug(:browser) plug(:browser)
plug(:authenticate) plug(:authenticate)
@ -167,8 +233,6 @@ defmodule Pleroma.Web.Router do
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
pipe_through([:admin_api, :require_admin]) pipe_through([:admin_api, :require_admin])
put("/users/disable_mfa", AdminAPIController, :disable_mfa)
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)
@ -199,17 +263,10 @@ defmodule Pleroma.Web.Router do
post("/relay", RelayController, :follow) post("/relay", RelayController, :follow)
delete("/relay", RelayController, :unfollow) delete("/relay", RelayController, :unfollow)
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("/instance_document/:name", InstanceDocumentController, :show) get("/instance_document/:name", InstanceDocumentController, :show)
patch("/instance_document/:name", InstanceDocumentController, :update) patch("/instance_document/:name", InstanceDocumentController, :update)
delete("/instance_document/:name", InstanceDocumentController, :delete) delete("/instance_document/:name", InstanceDocumentController, :delete)
patch("/users/confirm_email", AdminAPIController, :confirm_email)
patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email)
get("/config", ConfigController, :show) get("/config", ConfigController, :show)
post("/config", ConfigController, :update) post("/config", ConfigController, :update)
get("/config/descriptions", ConfigController, :descriptions) get("/config/descriptions", ConfigController, :descriptions)
@ -229,6 +286,11 @@ defmodule Pleroma.Web.Router do
post("/frontends/install", FrontendController, :install) post("/frontends/install", FrontendController, :install)
post("/backups", AdminAPIController, :create_backup) post("/backups", AdminAPIController, :create_backup)
end
# AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
pipe_through(:require_privileged_role_announcements_manage_announcements)
get("/announcements", AnnouncementController, :index) get("/announcements", AnnouncementController, :index)
post("/announcements", AnnouncementController, :create) post("/announcements", AnnouncementController, :create)
@ -237,14 +299,29 @@ defmodule Pleroma.Web.Router do
delete("/announcements/:id", AnnouncementController, :delete) delete("/announcements/:id", AnnouncementController, :delete)
end end
# AdminAPI: admins and mods (staff) can perform these actions (if enabled by config) # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
pipe_through([:admin_api, :require_privileged_staff]) pipe_through(:require_privileged_role_users_delete)
delete("/users", UserController, :delete) delete("/users", UserController, :delete)
end
# AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
pipe_through(:require_privileged_role_users_manage_credentials)
get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset) get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials)
patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials) patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials)
put("/users/disable_mfa", AdminAPIController, :disable_mfa)
patch("/users/force_password_reset", AdminAPIController, :force_password_reset)
patch("/users/confirm_email", AdminAPIController, :confirm_email)
patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email)
end
# AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
pipe_through(:require_privileged_role_messages_read)
get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses) get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
get("/users/:nickname/chats", AdminAPIController, :list_user_chats) get("/users/:nickname/chats", AdminAPIController, :list_user_chats)
@ -253,52 +330,100 @@ defmodule Pleroma.Web.Router do
get("/chats/:id", ChatController, :show) get("/chats/:id", ChatController, :show)
get("/chats/:id/messages", ChatController, :messages) get("/chats/:id/messages", ChatController, :messages)
get("/instances/:instance/statuses", InstanceController, :list_statuses)
get("/statuses/:id", StatusController, :show)
end end
# AdminAPI: admins and mods (staff) can perform these actions # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
pipe_through(:admin_api) pipe_through(:require_privileged_role_users_manage_tags)
put("/users/tag", AdminAPIController, :tag_users) put("/users/tag", AdminAPIController, :tag_users)
delete("/users/tag", AdminAPIController, :untag_users) delete("/users/tag", AdminAPIController, :untag_users)
end
# AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
pipe_through(:require_privileged_role_users_manage_activation_state)
patch("/users/:nickname/toggle_activation", UserController, :toggle_activation) patch("/users/:nickname/toggle_activation", UserController, :toggle_activation)
patch("/users/activate", UserController, :activate) patch("/users/activate", UserController, :activate)
patch("/users/deactivate", UserController, :deactivate) patch("/users/deactivate", UserController, :deactivate)
patch("/users/approve", UserController, :approve) end
# AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
pipe_through(:require_privileged_role_users_manage_invites)
patch("/users/approve", UserController, :approve)
post("/users/invite_token", InviteController, :create) post("/users/invite_token", InviteController, :create)
get("/users/invites", InviteController, :index) get("/users/invites", InviteController, :index)
post("/users/revoke_invite", InviteController, :revoke) post("/users/revoke_invite", InviteController, :revoke)
post("/users/email_invite", InviteController, :email) post("/users/email_invite", InviteController, :email)
end
get("/users", UserController, :index) # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
get("/users/:nickname", UserController, :show) scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
pipe_through(:require_privileged_role_reports_manage_reports)
get("/instances/:instance/statuses", InstanceController, :list_statuses)
delete("/instances/:instance", InstanceController, :delete)
get("/reports", ReportController, :index) get("/reports", ReportController, :index)
get("/reports/:id", ReportController, :show) get("/reports/:id", ReportController, :show)
patch("/reports", ReportController, :update) patch("/reports", ReportController, :update)
post("/reports/:id/notes", ReportController, :notes_create) post("/reports/:id/notes", ReportController, :notes_create)
delete("/reports/:report_id/notes/:id", ReportController, :notes_delete) delete("/reports/:report_id/notes/:id", ReportController, :notes_delete)
end
# AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
pipe_through(:require_privileged_role_users_read)
get("/users", UserController, :index)
get("/users/:nickname", UserController, :show)
end
# AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
pipe_through(:require_privileged_role_messages_delete)
get("/statuses/:id", StatusController, :show)
put("/statuses/:id", StatusController, :update) put("/statuses/:id", StatusController, :update)
delete("/statuses/:id", StatusController, :delete) delete("/statuses/:id", StatusController, :delete)
get("/moderation_log", AdminAPIController, :list_log)
post("/reload_emoji", AdminAPIController, :reload_emoji)
get("/stats", AdminAPIController, :stats)
delete("/chats/:id/messages/:message_id", ChatController, :delete_message) delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
end end
# AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
pipe_through(:require_privileged_role_emoji_manage_emoji)
post("/reload_emoji", AdminAPIController, :reload_emoji)
end
# AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
pipe_through(:require_privileged_role_instances_delete)
delete("/instances/:instance", InstanceController, :delete)
end
# AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
pipe_through(:require_privileged_role_moderation_log_read)
get("/moderation_log", AdminAPIController, :list_log)
end
# AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
pipe_through(:require_privileged_role_statistics_read)
get("/stats", AdminAPIController, :stats)
end
scope "/api/v1/pleroma/emoji", Pleroma.Web.PleromaAPI do scope "/api/v1/pleroma/emoji", Pleroma.Web.PleromaAPI do
scope "/pack" do scope "/pack" do
pipe_through(:admin_api) pipe_through(:require_privileged_role_emoji_manage_emoji)
post("/", EmojiPackController, :create) post("/", EmojiPackController, :create)
patch("/", EmojiPackController, :update) patch("/", EmojiPackController, :update)
@ -313,7 +438,7 @@ defmodule Pleroma.Web.Router do
# Modifying packs # Modifying packs
scope "/packs" do scope "/packs" do
pipe_through(:admin_api) pipe_through(:require_privileged_role_emoji_manage_emoji)
get("/import", EmojiPackController, :import_from_filesystem) get("/import", EmojiPackController, :import_from_filesystem)
get("/remote", EmojiPackController, :remote) get("/remote", EmojiPackController, :remote)

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.NotificationTest do defmodule Pleroma.NotificationTest do
use Pleroma.DataCase use Pleroma.DataCase, async: false
import Pleroma.Factory import Pleroma.Factory
import Mock import Mock
@ -32,20 +32,26 @@ defmodule Pleroma.NotificationTest do
refute {:ok, [nil]} == Notification.create_notifications(activity) refute {:ok, [nil]} == Notification.create_notifications(activity)
end end
test "creates a notification for a report" do test "creates a report notification only for privileged users" do
reporting_user = insert(:user) reporting_user = insert(:user)
reported_user = insert(:user) reported_user = insert(:user)
{:ok, moderator_user} = insert(:user) |> User.admin_api_update(%{is_moderator: true}) moderator_user = insert(:user, is_moderator: true)
{:ok, activity} = CommonAPI.report(reporting_user, %{account_id: reported_user.id}) clear_config([:instance, :moderator_privileges], [])
{:ok, activity1} = CommonAPI.report(reporting_user, %{account_id: reported_user.id})
{:ok, []} = Notification.create_notifications(activity1)
{:ok, [notification]} = Notification.create_notifications(activity) clear_config([:instance, :moderator_privileges], [:reports_manage_reports])
{:ok, activity2} = CommonAPI.report(reporting_user, %{account_id: reported_user.id})
{:ok, [notification]} = Notification.create_notifications(activity2)
assert notification.user_id == moderator_user.id assert notification.user_id == moderator_user.id
assert notification.type == "pleroma:report" assert notification.type == "pleroma:report"
end end
test "suppresses notification to reporter if reporter is an admin" do test "suppresses notifications for own reports" do
clear_config([:instance, :admin_privileges], [:reports_manage_reports])
reporting_admin = insert(:user, is_admin: true) reporting_admin = insert(:user, is_admin: true)
reported_user = insert(:user) reported_user = insert(:user)
other_admin = insert(:user, is_admin: true) other_admin = insert(:user, is_admin: true)
@ -542,25 +548,6 @@ defmodule Pleroma.NotificationTest do
end end
end end
describe "destroy_multiple_from_types/2" do
test "clears all notifications of a certain type for a given user" do
report_activity = insert(:report_activity)
user1 = insert(:user, is_moderator: true, is_admin: true)
user2 = insert(:user, is_moderator: true, is_admin: true)
{:ok, _} = Notification.create_notifications(report_activity)
{:ok, _} =
CommonAPI.post(user2, %{
status: "hey @#{user1.nickname} !"
})
Notification.destroy_multiple_from_types(user1, ["pleroma:report"])
assert [%Pleroma.Notification{type: "mention"}] = Notification.for_user(user1)
assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user2)
end
end
describe "set_read_up_to()" do describe "set_read_up_to()" do
test "it sets all notifications as read up to a specified notification ID" do test "it sets all notifications as read up to a specified notification ID" do
user = insert(:user) user = insert(:user)

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.QueryTest do defmodule Pleroma.User.QueryTest do
use Pleroma.DataCase, async: true use Pleroma.DataCase, async: false
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.User alias Pleroma.User
@ -44,4 +44,63 @@ defmodule Pleroma.User.QueryTest do
|> User.Query.build() |> User.Query.build()
|> Repo.all() |> Repo.all()
end end
describe "is_privileged param" do
setup do
%{
user: insert(:user, local: true, is_admin: false, is_moderator: false),
moderator_user: insert(:user, local: true, is_admin: false, is_moderator: true),
admin_user: insert(:user, local: true, is_admin: true, is_moderator: false),
admin_moderator_user: insert(:user, local: true, is_admin: true, is_moderator: true),
remote_user: insert(:user, local: false, is_admin: true, is_moderator: true),
non_active_user:
insert(:user, local: true, is_admin: true, is_moderator: true, is_active: false)
}
end
test "doesn't return any users when there are no privileged roles" do
clear_config([:instance, :admin_privileges], [])
clear_config([:instance, :moderator_privileges], [])
assert [] = User.Query.build(%{is_privileged: :cofe}) |> Repo.all()
end
test "returns moderator users if they are privileged", %{
moderator_user: moderator_user,
admin_moderator_user: admin_moderator_user
} do
clear_config([:instance, :admin_privileges], [])
clear_config([:instance, :moderator_privileges], [:cofe])
assert [_, _] = User.Query.build(%{is_privileged: :cofe}) |> Repo.all()
assert moderator_user in (User.Query.build(%{is_privileged: :cofe}) |> Repo.all())
assert admin_moderator_user in (User.Query.build(%{is_privileged: :cofe}) |> Repo.all())
end
test "returns admin users if they are privileged", %{
admin_user: admin_user,
admin_moderator_user: admin_moderator_user
} do
clear_config([:instance, :admin_privileges], [:cofe])
clear_config([:instance, :moderator_privileges], [])
assert [_, _] = User.Query.build(%{is_privileged: :cofe}) |> Repo.all()
assert admin_user in (User.Query.build(%{is_privileged: :cofe}) |> Repo.all())
assert admin_moderator_user in (User.Query.build(%{is_privileged: :cofe}) |> Repo.all())
end
test "returns admin and moderator users if they are both privileged", %{
moderator_user: moderator_user,
admin_user: admin_user,
admin_moderator_user: admin_moderator_user
} do
clear_config([:instance, :admin_privileges], [:cofe])
clear_config([:instance, :moderator_privileges], [:cofe])
assert [_, _, _] = User.Query.build(%{is_privileged: :cofe}) |> Repo.all()
assert admin_user in (User.Query.build(%{is_privileged: :cofe}) |> Repo.all())
assert moderator_user in (User.Query.build(%{is_privileged: :cofe}) |> Repo.all())
assert admin_moderator_user in (User.Query.build(%{is_privileged: :cofe}) |> Repo.all())
end
end
end end

View file

@ -5,7 +5,6 @@
defmodule Pleroma.UserTest do defmodule Pleroma.UserTest do
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Builders.UserBuilder alias Pleroma.Builders.UserBuilder
alias Pleroma.Notification
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.Tests.ObanHelpers alias Pleroma.Tests.ObanHelpers
@ -13,7 +12,7 @@ defmodule Pleroma.UserTest do
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
use Pleroma.DataCase use Pleroma.DataCase, async: false
use Oban.Testing, repo: Pleroma.Repo use Oban.Testing, repo: Pleroma.Repo
import Pleroma.Factory import Pleroma.Factory
@ -473,12 +472,7 @@ defmodule Pleroma.UserTest do
reject_deletes: [] reject_deletes: []
) )
setup do: setup do: clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])
clear_config(:mrf,
policies: [
Pleroma.Web.ActivityPub.MRF.SimplePolicy
]
)
test "it sends a welcome chat message when Simple policy applied to local instance" do test "it sends a welcome chat message when Simple policy applied to local instance" do
clear_config([:mrf_simple, :media_nsfw], [{"localhost", ""}]) clear_config([:mrf_simple, :media_nsfw], [{"localhost", ""}])
@ -2038,31 +2032,82 @@ defmodule Pleroma.UserTest do
end end
end end
describe "superuser?/1" do describe "privileged?/1" do
setup do
clear_config([:instance, :admin_privileges], [:cofe, :suya])
clear_config([:instance, :moderator_privileges], [:cofe, :suya])
end
test "returns false for unprivileged users" do test "returns false for unprivileged users" do
user = insert(:user, local: true) user = insert(:user, local: true)
refute User.superuser?(user) refute User.privileged?(user, :cofe)
end end
test "returns false for remote users" do test "returns false for remote users" do
user = insert(:user, local: false) user = insert(:user, local: false)
remote_admin_user = insert(:user, local: false, is_admin: true) remote_admin_user = insert(:user, local: false, is_admin: true)
refute User.superuser?(user) refute User.privileged?(user, :cofe)
refute User.superuser?(remote_admin_user) refute User.privileged?(remote_admin_user, :cofe)
end end
test "returns true for local moderators" do test "returns true for local moderators if, and only if, they are privileged" do
user = insert(:user, local: true, is_moderator: true) user = insert(:user, local: true, is_moderator: true)
assert User.superuser?(user) assert User.privileged?(user, :cofe)
clear_config([:instance, :moderator_privileges], [])
refute User.privileged?(user, :cofe)
end end
test "returns true for local admins" do test "returns true for local admins if, and only if, they are privileged" do
user = insert(:user, local: true, is_admin: true) user = insert(:user, local: true, is_admin: true)
assert User.superuser?(user) assert User.privileged?(user, :cofe)
clear_config([:instance, :admin_privileges], [])
refute User.privileged?(user, :cofe)
end
end
describe "privileges/1" do
setup do
clear_config([:instance, :moderator_privileges], [:cofe, :only_moderator])
clear_config([:instance, :admin_privileges], [:cofe, :only_admin])
end
test "returns empty list for users without roles" do
user = insert(:user, local: true)
assert [] == User.privileges(user)
end
test "returns list of privileges for moderators" do
moderator = insert(:user, is_moderator: true, local: true)
assert [:cofe, :only_moderator] == User.privileges(moderator) |> Enum.sort()
end
test "returns list of privileges for admins" do
admin = insert(:user, is_admin: true, local: true)
assert [:cofe, :only_admin] == User.privileges(admin) |> Enum.sort()
end
test "returns list of unique privileges for users who are both moderator and admin" do
moderator_admin = insert(:user, is_moderator: true, is_admin: true, local: true)
assert [:cofe, :only_admin, :only_moderator] ==
User.privileges(moderator_admin) |> Enum.sort()
end
test "returns empty list for remote users" do
remote_moderator_admin = insert(:user, is_moderator: true, is_admin: true, local: false)
assert [] == User.privileges(remote_moderator_admin)
end end
end end
@ -2105,13 +2150,77 @@ defmodule Pleroma.UserTest do
assert User.visible_for(user, other_user) == :visible assert User.visible_for(user, other_user) == :visible
end end
test "returns true when the account is unconfirmed and being viewed by a privileged account (confirmation required)" do test "returns true when the account is unconfirmed and being viewed by a privileged account (privilege :users_manage_activation_state, confirmation required)" do
clear_config([:instance, :account_activation_required], true) clear_config([:instance, :account_activation_required], true)
clear_config([:instance, :admin_privileges], [:users_manage_activation_state])
user = insert(:user, local: true, is_confirmed: false) user = insert(:user, local: true, is_confirmed: false)
other_user = insert(:user, local: true, is_admin: true) other_user = insert(:user, local: true, is_admin: true)
assert User.visible_for(user, other_user) == :visible assert User.visible_for(user, other_user) == :visible
clear_config([:instance, :admin_privileges], [])
refute User.visible_for(user, other_user) == :visible
end
end
describe "all_users_with_privilege/1" do
setup do
%{
user: insert(:user, local: true, is_admin: false, is_moderator: false),
moderator_user: insert(:user, local: true, is_admin: false, is_moderator: true),
admin_user: insert(:user, local: true, is_admin: true, is_moderator: false),
admin_moderator_user: insert(:user, local: true, is_admin: true, is_moderator: true),
remote_user: insert(:user, local: false, is_admin: true, is_moderator: true),
non_active_user:
insert(:user, local: true, is_admin: true, is_moderator: true, is_active: false)
}
end
test "doesn't return any users when there are no privileged roles" do
clear_config([:instance, :admin_privileges], [])
clear_config([:instance, :moderator_privileges], [])
assert [] = User.Query.build(%{is_privileged: :cofe}) |> Repo.all()
end
test "returns moderator users if they are privileged", %{
moderator_user: moderator_user,
admin_moderator_user: admin_moderator_user
} do
clear_config([:instance, :admin_privileges], [])
clear_config([:instance, :moderator_privileges], [:cofe])
assert [_, _] = User.Query.build(%{is_privileged: :cofe}) |> Repo.all()
assert moderator_user in User.all_users_with_privilege(:cofe)
assert admin_moderator_user in User.all_users_with_privilege(:cofe)
end
test "returns admin users if they are privileged", %{
admin_user: admin_user,
admin_moderator_user: admin_moderator_user
} do
clear_config([:instance, :admin_privileges], [:cofe])
clear_config([:instance, :moderator_privileges], [])
assert [_, _] = User.Query.build(%{is_privileged: :cofe}) |> Repo.all()
assert admin_user in User.all_users_with_privilege(:cofe)
assert admin_moderator_user in User.all_users_with_privilege(:cofe)
end
test "returns admin and moderator users if they are both privileged", %{
moderator_user: moderator_user,
admin_user: admin_user,
admin_moderator_user: admin_moderator_user
} do
clear_config([:instance, :admin_privileges], [:cofe])
clear_config([:instance, :moderator_privileges], [:cofe])
assert [_, _, _] = User.Query.build(%{is_privileged: :cofe}) |> Repo.all()
assert admin_user in User.all_users_with_privilege(:cofe)
assert moderator_user in User.all_users_with_privilege(:cofe)
assert admin_moderator_user in User.all_users_with_privilege(:cofe)
end end
end end
@ -2351,26 +2460,6 @@ defmodule Pleroma.UserTest do
assert {:ok, user} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}") assert {:ok, user} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
assert %User{bio: "test-bio"} = User.get_cached_by_ap_id(user.ap_id) assert %User{bio: "test-bio"} = User.get_cached_by_ap_id(user.ap_id)
end end
test "removes report notifs when user isn't superuser any more" do
report_activity = insert(:report_activity)
user = insert(:user, is_moderator: true, is_admin: true)
{:ok, _} = Notification.create_notifications(report_activity)
assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user)
{:ok, user} = user |> User.admin_api_update(%{is_moderator: false})
# is still superuser because still admin
assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user)
{:ok, user} = user |> User.admin_api_update(%{is_moderator: true, is_admin: false})
# is still superuser because still moderator
assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user)
{:ok, user} = user |> User.admin_api_update(%{is_moderator: false})
# is not a superuser any more
assert [] = Notification.for_user(user)
end
end end
describe "following/followers synchronization" do describe "following/followers synchronization" do

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidationTest do defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidationTest do
use Pleroma.DataCase, async: true use Pleroma.DataCase, async: false
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Web.ActivityPub.Builder alias Pleroma.Web.ActivityPub.Builder
@ -90,17 +90,26 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidationTest do
assert {:actor, {"is not allowed to modify object", []}} in cng.errors assert {:actor, {"is not allowed to modify object", []}} in cng.errors
end end
test "it's valid if the actor of the object is a local superuser", test "it's only valid if the actor of the object is a privileged local user",
%{valid_post_delete: valid_post_delete} do %{valid_post_delete: valid_post_delete} do
clear_config([:instance, :moderator_privileges], [:messages_delete])
user = user =
insert(:user, local: true, is_moderator: true, ap_id: "https://gensokyo.2hu/users/raymoo") insert(:user, local: true, is_moderator: true, ap_id: "https://gensokyo.2hu/users/raymoo")
valid_other_actor = post_delete_with_moderator_actor =
valid_post_delete valid_post_delete
|> Map.put("actor", user.ap_id) |> Map.put("actor", user.ap_id)
{:ok, _, meta} = ObjectValidator.validate(valid_other_actor, []) {:ok, _, meta} = ObjectValidator.validate(post_delete_with_moderator_actor, [])
assert meta[:do_not_federate] assert meta[:do_not_federate]
clear_config([:instance, :moderator_privileges], [])
{:error, cng} = ObjectValidator.validate(post_delete_with_moderator_actor, [])
assert {:actor, {"is not allowed to modify object", []}} in cng.errors
end end
end end
end end

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase, async: false
use Oban.Testing, repo: Pleroma.Repo use Oban.Testing, repo: Pleroma.Repo
import ExUnit.CaptureLog import ExUnit.CaptureLog
@ -92,18 +92,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
describe "PUT /api/pleroma/admin/users/tag" do describe "PUT /api/pleroma/admin/users/tag" do
setup %{conn: conn} do setup %{conn: conn} do
clear_config([:instance, :admin_privileges], [:users_manage_tags])
user1 = insert(:user, %{tags: ["x"]}) user1 = insert(:user, %{tags: ["x"]})
user2 = insert(:user, %{tags: ["y"]}) user2 = insert(:user, %{tags: ["y"]})
user3 = insert(:user, %{tags: ["unchanged"]}) user3 = insert(:user, %{tags: ["unchanged"]})
conn =
conn
|> put_req_header("accept", "application/json")
|> put(
"/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
"#{user2.nickname}&tags[]=foo&tags[]=bar"
)
%{conn: conn, user1: user1, user2: user2, user3: user3} %{conn: conn, user1: user1, user2: user2, user3: user3}
end end
@ -113,6 +107,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
user1: user1, user1: user1,
user2: user2 user2: user2
} do } do
conn =
conn
|> put_req_header("accept", "application/json")
|> put(
"/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
"#{user2.nickname}&tags[]=foo&tags[]=bar"
)
assert empty_json_response(conn) assert empty_json_response(conn)
assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"] assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"] assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"]
@ -130,26 +132,43 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"@#{admin.nickname} added tags: #{tags} to users: #{users}" "@#{admin.nickname} added tags: #{tags} to users: #{users}"
end end
test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do test "it does not modify tags of not specified users", %{
conn: conn,
user1: user1,
user2: user2,
user3: user3
} do
conn =
conn
|> put_req_header("accept", "application/json")
|> put(
"/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
"#{user2.nickname}&tags[]=foo&tags[]=bar"
)
assert empty_json_response(conn) assert empty_json_response(conn)
assert User.get_cached_by_id(user3.id).tags == ["unchanged"] assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
end end
test "it requires privileged role :users_manage_tags", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
response =
conn
|> put_req_header("accept", "application/json")
|> put("/api/pleroma/admin/users/tag?nicknames[]=nickname&tags[]=foo&tags[]=bar")
assert json_response(response, :forbidden)
end
end end
describe "DELETE /api/pleroma/admin/users/tag" do describe "DELETE /api/pleroma/admin/users/tag" do
setup %{conn: conn} do setup %{conn: conn} do
clear_config([:instance, :admin_privileges], [:users_manage_tags])
user1 = insert(:user, %{tags: ["x"]}) user1 = insert(:user, %{tags: ["x"]})
user2 = insert(:user, %{tags: ["y", "z"]}) user2 = insert(:user, %{tags: ["y", "z"]})
user3 = insert(:user, %{tags: ["unchanged"]}) user3 = insert(:user, %{tags: ["unchanged"]})
conn =
conn
|> put_req_header("accept", "application/json")
|> delete(
"/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
"#{user2.nickname}&tags[]=x&tags[]=z"
)
%{conn: conn, user1: user1, user2: user2, user3: user3} %{conn: conn, user1: user1, user2: user2, user3: user3}
end end
@ -159,6 +178,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
user1: user1, user1: user1,
user2: user2 user2: user2
} do } do
conn =
conn
|> put_req_header("accept", "application/json")
|> delete(
"/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
"#{user2.nickname}&tags[]=x&tags[]=z"
)
assert empty_json_response(conn) assert empty_json_response(conn)
assert User.get_cached_by_id(user1.id).tags == [] assert User.get_cached_by_id(user1.id).tags == []
assert User.get_cached_by_id(user2.id).tags == ["y"] assert User.get_cached_by_id(user2.id).tags == ["y"]
@ -176,10 +203,34 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"@#{admin.nickname} removed tags: #{tags} from users: #{users}" "@#{admin.nickname} removed tags: #{tags} from users: #{users}"
end end
test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do test "it does not modify tags of not specified users", %{
conn: conn,
user1: user1,
user2: user2,
user3: user3
} do
conn =
conn
|> put_req_header("accept", "application/json")
|> delete(
"/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
"#{user2.nickname}&tags[]=x&tags[]=z"
)
assert empty_json_response(conn) assert empty_json_response(conn)
assert User.get_cached_by_id(user3.id).tags == ["unchanged"] assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
end end
test "it requires privileged role :users_manage_tags", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
response =
conn
|> put_req_header("accept", "application/json")
|> delete("/api/pleroma/admin/users/tag?nicknames[]=nickname&tags[]=foo&tags[]=bar")
assert json_response(response, :forbidden)
end
end end
describe "/api/pleroma/admin/users/:nickname/permission_group" do describe "/api/pleroma/admin/users/:nickname/permission_group" do
@ -271,21 +322,38 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
end end
end end
test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do describe "/api/pleroma/admin/users/:nickname/password_reset" do
user = insert(:user) test "it returns a password reset link", %{conn: conn} do
clear_config([:instance, :admin_privileges], [:users_manage_credentials])
conn = user = insert(:user)
conn
|> put_req_header("accept", "application/json")
|> get("/api/pleroma/admin/users/#{user.nickname}/password_reset")
resp = json_response(conn, 200) conn =
conn
|> put_req_header("accept", "application/json")
|> get("/api/pleroma/admin/users/#{user.nickname}/password_reset")
assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"]) resp = json_response(conn, 200)
assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"])
end
test "it requires privileged role :users_manage_credentials", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
response =
conn
|> put_req_header("accept", "application/json")
|> get("/api/pleroma/admin/users/nickname/password_reset")
assert json_response(response, :forbidden)
end
end end
describe "PUT disable_mfa" do describe "PUT disable_mfa" do
test "returns 200 and disable 2fa", %{conn: conn} do test "returns 200 and disable 2fa", %{conn: conn} do
clear_config([:instance, :admin_privileges], [:users_manage_credentials])
user = user =
insert(:user, insert(:user,
multi_factor_authentication_settings: %MFA.Settings{ multi_factor_authentication_settings: %MFA.Settings{
@ -307,6 +375,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
end end
test "returns 404 if user not found", %{conn: conn} do test "returns 404 if user not found", %{conn: conn} do
clear_config([:instance, :admin_privileges], [:users_manage_credentials])
response = response =
conn conn
|> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"}) |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"})
@ -314,6 +384,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert response == %{"error" => "Not found"} assert response == %{"error" => "Not found"}
end end
test "it requires privileged role :users_manage_credentials", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
response =
conn
|> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"})
assert json_response(response, :forbidden)
end
end end
describe "GET /api/pleroma/admin/restart" do describe "GET /api/pleroma/admin/restart" do
@ -344,6 +424,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
describe "GET /api/pleroma/admin/users/:nickname/statuses" do describe "GET /api/pleroma/admin/users/:nickname/statuses" do
setup do setup do
clear_config([:instance, :admin_privileges], [:messages_read])
user = insert(:user) user = insert(:user)
insert(:note_activity, user: user) insert(:note_activity, user: user)
@ -360,6 +442,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert length(activities) == 3 assert length(activities) == 3
end end
test "it requires privileged role :messages_read", %{conn: conn, user: user} do
clear_config([:instance, :admin_privileges], [])
conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
assert json_response(conn, :forbidden)
end
test "renders user's statuses with pagination", %{conn: conn, user: user} do test "renders user's statuses with pagination", %{conn: conn, user: user} do
%{"total" => 3, "activities" => [activity1]} = %{"total" => 3, "activities" => [activity1]} =
conn conn
@ -421,21 +511,32 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
describe "GET /api/pleroma/admin/users/:nickname/chats" do describe "GET /api/pleroma/admin/users/:nickname/chats" do
setup do setup do
clear_config([:instance, :admin_privileges], [:messages_read])
user = insert(:user) user = insert(:user)
%{user: user}
end
test "renders user's chats", %{conn: conn, user: user} do
recipients = insert_list(3, :user) recipients = insert_list(3, :user)
Enum.each(recipients, fn recipient -> Enum.each(recipients, fn recipient ->
CommonAPI.post_chat_message(user, recipient, "yo") CommonAPI.post_chat_message(user, recipient, "yo")
end) end)
%{user: user}
end
test "renders user's chats", %{conn: conn, user: user} do
conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/chats") conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/chats")
assert json_response(conn, 200) |> length() == 3 assert json_response(conn, 200) |> length() == 3
end end
test "it requires privileged role :messages_read", %{conn: conn, user: user} do
clear_config([:instance, :admin_privileges], [])
conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/chats")
assert json_response(conn, :forbidden)
end
end end
describe "GET /api/pleroma/admin/users/:nickname/chats unauthorized" do describe "GET /api/pleroma/admin/users/:nickname/chats unauthorized" do
@ -471,6 +572,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
describe "GET /api/pleroma/admin/moderation_log" do describe "GET /api/pleroma/admin/moderation_log" do
setup do setup do
clear_config([:instance, :admin_privileges], [:moderation_log_read])
moderator = insert(:user, is_moderator: true) moderator = insert(:user, is_moderator: true)
%{moderator: moderator} %{moderator: moderator}
@ -675,6 +777,15 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert get_in(first_entry, ["data", "message"]) == assert get_in(first_entry, ["data", "message"]) ==
"@#{moderator.nickname} unfollowed relay: https://example.org/relay" "@#{moderator.nickname} unfollowed relay: https://example.org/relay"
end end
test "it requires privileged role :moderation_log_read", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
assert conn
|> put_req_header("content-type", "multipart/form-data")
|> get("/api/pleroma/admin/moderation_log")
|> json_response(:forbidden)
end
end end
test "gets a remote users when [:instance, :limit_to_local_content] is set to :unauthenticated", test "gets a remote users when [:instance, :limit_to_local_content] is set to :unauthenticated",
@ -688,6 +799,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
describe "GET /users/:nickname/credentials" do describe "GET /users/:nickname/credentials" do
test "gets the user credentials", %{conn: conn} do test "gets the user credentials", %{conn: conn} do
clear_config([:instance, :admin_privileges], [:users_manage_credentials])
user = insert(:user) user = insert(:user)
conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials") conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
@ -696,6 +808,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
end end
test "returns 403 if requested by a non-admin" do test "returns 403 if requested by a non-admin" do
clear_config([:instance, :admin_privileges], [:users_manage_credentials])
user = insert(:user) user = insert(:user)
conn = conn =
@ -705,6 +818,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert json_response(conn, :forbidden) assert json_response(conn, :forbidden)
end end
test "it requires privileged role :users_manage_credentials", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
response =
conn
|> get("/api/pleroma/admin/users/nickname/credentials")
assert json_response(response, :forbidden)
end
end end
describe "PATCH /users/:nickname/credentials" do describe "PATCH /users/:nickname/credentials" do
@ -714,6 +837,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
end end
test "changes password and email", %{conn: conn, admin: admin, user: user} do test "changes password and email", %{conn: conn, admin: admin, user: user} do
clear_config([:instance, :admin_privileges], [:users_manage_credentials])
assert user.password_reset_pending == false assert user.password_reset_pending == false
conn = conn =
@ -756,6 +881,19 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert json_response(conn, :forbidden) assert json_response(conn, :forbidden)
end end
test "returns 403 if not privileged with :users_manage_credentials", %{conn: conn, user: user} do
clear_config([:instance, :admin_privileges], [])
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, :forbidden)
end
test "changes actor type from permitted list", %{conn: conn, user: user} do test "changes actor type from permitted list", %{conn: conn, user: user} do
assert user.actor_type == "Person" assert user.actor_type == "Person"
@ -784,6 +922,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
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
clear_config([:instance, :admin_privileges], [:users_manage_credentials])
user = insert(:user) user = insert(:user)
assert user.password_reset_pending == false assert user.password_reset_pending == false
@ -796,10 +935,21 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert User.get_by_id(user.id).password_reset_pending == true assert User.get_by_id(user.id).password_reset_pending == true
end end
test "it requires privileged role :users_manage_credentials", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
response =
conn
|> patch("/api/pleroma/admin/users/force_password_reset", %{nickname: "nickname"})
assert json_response(response, :forbidden)
end
end end
describe "PATCH /confirm_email" do describe "PATCH /confirm_email" do
test "it confirms emails of two users", %{conn: conn, admin: admin} do test "it confirms emails of two users", %{conn: conn, admin: admin} do
clear_config([:instance, :admin_privileges], [:users_manage_credentials])
[first_user, second_user] = insert_pair(:user, is_confirmed: false) [first_user, second_user] = insert_pair(:user, is_confirmed: false)
refute first_user.is_confirmed refute first_user.is_confirmed
@ -826,10 +976,21 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert ModerationLog.get_log_entry_message(log_entry) == assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{second_user.nickname}" "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{second_user.nickname}"
end end
test "it requires privileged role :users_manage_credentials", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
response =
conn
|> patch("/api/pleroma/admin/users/confirm_email", %{nicknames: ["nickname"]})
assert json_response(response, :forbidden)
end
end end
describe "PATCH /resend_confirmation_email" do describe "PATCH /resend_confirmation_email" do
test "it resend emails for two users", %{conn: conn, admin: admin} do test "it resend emails for two users", %{conn: conn, admin: admin} do
clear_config([:instance, :admin_privileges], [:users_manage_credentials])
[first_user, second_user] = insert_pair(:user, is_confirmed: false) [first_user, second_user] = insert_pair(:user, is_confirmed: false)
ret_conn = ret_conn =
@ -855,9 +1016,23 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|> Swoosh.Email.put_private(:hackney_options, ssl_options: [versions: [:"tlsv1.2"]]) |> Swoosh.Email.put_private(:hackney_options, ssl_options: [versions: [:"tlsv1.2"]])
|> assert_email_sent() |> assert_email_sent()
end end
test "it requires privileged role :users_manage_credentials", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
response =
conn
|> patch("/api/pleroma/admin/users/resend_confirmation_email", %{nicknames: ["nickname"]})
assert json_response(response, :forbidden)
end
end end
describe "/api/pleroma/admin/stats" do describe "/api/pleroma/admin/stats" do
setup do
clear_config([:instance, :admin_privileges], [:statistics_read])
end
test "status visibility count", %{conn: conn} do test "status visibility count", %{conn: conn} do
user = insert(:user) user = insert(:user)
CommonAPI.post(user, %{visibility: "public", status: "hey"}) CommonAPI.post(user, %{visibility: "public", status: "hey"})
@ -890,6 +1065,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert %{"direct" => 0, "private" => 1, "public" => 0, "unlisted" => 1} = assert %{"direct" => 0, "private" => 1, "public" => 0, "unlisted" => 1} =
response["status_visibility"] response["status_visibility"]
end end
test "it requires privileged role :statistics_read", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
assert conn
|> get("/api/pleroma/admin/stats", instance: "lain.wired")
|> json_response(:forbidden)
end
end end
describe "/api/pleroma/backups" do describe "/api/pleroma/backups" do
@ -958,6 +1141,34 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert Repo.aggregate(Pleroma.User.Backup, :count) == 2 assert Repo.aggregate(Pleroma.User.Backup, :count) == 2
end end
end end
describe "POST /api/v1/pleroma/admin/reload_emoji" do
setup do
clear_config([:instance, :admin_privileges], [:emoji_manage_emoji])
admin = insert(:user, is_admin: true)
token = insert(:oauth_admin_token, user: admin)
conn =
build_conn()
|> assign(:user, admin)
|> assign(:token, token)
{:ok, %{conn: conn, admin: admin}}
end
test "it requires privileged role :emoji_manage_emoji", %{conn: conn} do
assert conn
|> post("/api/v1/pleroma/admin/reload_emoji")
|> json_response(200)
clear_config([:instance, :admin_privileges], [])
assert conn
|> post("/api/v1/pleroma/admin/reload_emoji")
|> json_response(:forbidden)
end
end
end end
# Needed for testing # Needed for testing

View file

@ -3,11 +3,12 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.AnnouncementControllerTest do defmodule Pleroma.Web.AdminAPI.AnnouncementControllerTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase, async: false
import Pleroma.Factory import Pleroma.Factory
setup do setup do
clear_config([:instance, :admin_privileges], [:announcements_manage_announcements])
admin = insert(:user, is_admin: true) admin = insert(:user, is_admin: true)
token = insert(:oauth_admin_token, user: admin) token = insert(:oauth_admin_token, user: admin)
@ -31,6 +32,18 @@ defmodule Pleroma.Web.AdminAPI.AnnouncementControllerTest do
assert [%{"id" => ^id}] = response assert [%{"id" => ^id}] = response
end end
test "it requires privileged role :announcements_manage_announcements", %{conn: conn} do
conn
|> get("/api/v1/pleroma/admin/announcements")
|> json_response_and_validate_schema(:ok)
clear_config([:instance, :admin_privileges], [])
conn
|> get("/api/v1/pleroma/admin/announcements")
|> json_response(:forbidden)
end
test "it paginates announcements", %{conn: conn} do test "it paginates announcements", %{conn: conn} do
_announcements = Enum.map(0..20, fn _ -> insert(:announcement) end) _announcements = Enum.map(0..20, fn _ -> insert(:announcement) end)
@ -92,6 +105,20 @@ defmodule Pleroma.Web.AdminAPI.AnnouncementControllerTest do
assert %{"id" => ^id} = response assert %{"id" => ^id} = response
end end
test "it requires privileged role :announcements_manage_announcements", %{conn: conn} do
%{id: id} = insert(:announcement)
conn
|> get("/api/v1/pleroma/admin/announcements/#{id}")
|> json_response_and_validate_schema(:ok)
clear_config([:instance, :admin_privileges], [])
conn
|> get("/api/v1/pleroma/admin/announcements/#{id}")
|> json_response(:forbidden)
end
test "it returns not found for non-existent id", %{conn: conn} do test "it returns not found for non-existent id", %{conn: conn} do
%{id: id} = insert(:announcement) %{id: id} = insert(:announcement)
@ -112,6 +139,20 @@ defmodule Pleroma.Web.AdminAPI.AnnouncementControllerTest do
|> json_response_and_validate_schema(:ok) |> json_response_and_validate_schema(:ok)
end end
test "it requires privileged role :announcements_manage_announcements", %{conn: conn} do
%{id: id} = insert(:announcement)
conn
|> delete("/api/v1/pleroma/admin/announcements/#{id}")
|> json_response_and_validate_schema(:ok)
clear_config([:instance, :admin_privileges], [])
conn
|> delete("/api/v1/pleroma/admin/announcements/#{id}")
|> json_response(:forbidden)
end
test "it returns not found for non-existent id", %{conn: conn} do test "it returns not found for non-existent id", %{conn: conn} do
%{id: id} = insert(:announcement) %{id: id} = insert(:announcement)
@ -156,6 +197,29 @@ defmodule Pleroma.Web.AdminAPI.AnnouncementControllerTest do
assert NaiveDateTime.compare(new.starts_at, starts_at) == :eq assert NaiveDateTime.compare(new.starts_at, starts_at) == :eq
end end
test "it requires privileged role :announcements_manage_announcements", %{conn: conn} do
%{id: id} = insert(:announcement)
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
starts_at = NaiveDateTime.add(now, -10, :second)
conn
|> put_req_header("content-type", "application/json")
|> patch("/api/v1/pleroma/admin/announcements/#{id}", %{
starts_at: NaiveDateTime.to_iso8601(starts_at)
})
|> json_response_and_validate_schema(:ok)
clear_config([:instance, :admin_privileges], [])
conn
|> put_req_header("content-type", "application/json")
|> patch("/api/v1/pleroma/admin/announcements/#{id}", %{
starts_at: NaiveDateTime.to_iso8601(starts_at)
})
|> json_response(:forbidden)
end
test "it updates with time with utc timezone", %{conn: conn} do test "it updates with time with utc timezone", %{conn: conn} do
%{id: id} = insert(:announcement) %{id: id} = insert(:announcement)
@ -250,6 +314,36 @@ defmodule Pleroma.Web.AdminAPI.AnnouncementControllerTest do
assert NaiveDateTime.compare(announcement.ends_at, ends_at) == :eq assert NaiveDateTime.compare(announcement.ends_at, ends_at) == :eq
end end
test "it requires privileged role :announcements_manage_announcements", %{conn: conn} do
content = "test post announcement api"
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
starts_at = NaiveDateTime.add(now, -10, :second)
ends_at = NaiveDateTime.add(now, 10, :second)
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/admin/announcements", %{
"content" => content,
"starts_at" => NaiveDateTime.to_iso8601(starts_at),
"ends_at" => NaiveDateTime.to_iso8601(ends_at),
"all_day" => true
})
|> json_response_and_validate_schema(:ok)
clear_config([:instance, :admin_privileges], [])
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/admin/announcements", %{
"content" => content,
"starts_at" => NaiveDateTime.to_iso8601(starts_at),
"ends_at" => NaiveDateTime.to_iso8601(ends_at),
"all_day" => true
})
|> json_response(:forbidden)
end
test "creating with time with utc timezones", %{conn: conn} do test "creating with time with utc timezones", %{conn: conn} do
content = "test post announcement api" content = "test post announcement api"

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.ChatControllerTest do defmodule Pleroma.Web.AdminAPI.ChatControllerTest do
use Pleroma.Web.ConnCase, async: true use Pleroma.Web.ConnCase, async: false
import Pleroma.Factory import Pleroma.Factory
@ -27,7 +27,10 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do
end end
describe "DELETE /api/pleroma/admin/chats/:id/messages/:message_id" do describe "DELETE /api/pleroma/admin/chats/:id/messages/:message_id" do
setup do: admin_setup() setup do
clear_config([:instance, :admin_privileges], [:messages_delete])
admin_setup()
end
test "it deletes a message from the chat", %{conn: conn, admin: admin} do test "it deletes a message from the chat", %{conn: conn, admin: admin} do
user = insert(:user) user = insert(:user)
@ -60,10 +63,22 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do
refute MessageReference.get_by_id(recipient_cm_ref.id) refute MessageReference.get_by_id(recipient_cm_ref.id)
assert %{data: %{"type" => "Tombstone"}} = Object.get_by_id(object.id) assert %{data: %{"type" => "Tombstone"}} = Object.get_by_id(object.id)
end end
test "it requires privileged role :messages_delete", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
assert conn
|> put_req_header("content-type", "application/json")
|> delete("/api/pleroma/admin/chats/some_id/messages/some_ref_id")
|> json_response(:forbidden)
end
end end
describe "GET /api/pleroma/admin/chats/:id/messages" do describe "GET /api/pleroma/admin/chats/:id/messages" do
setup do: admin_setup() setup do
clear_config([:instance, :admin_privileges], [:messages_read])
admin_setup()
end
test "it paginates", %{conn: conn} do test "it paginates", %{conn: conn} do
user = insert(:user) user = insert(:user)
@ -114,10 +129,21 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do
assert length(result) == 3 assert length(result) == 3
end end
test "it requires privileged role :messages_read", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn = get(conn, "/api/pleroma/admin/chats/some_id/messages")
assert json_response(conn, :forbidden)
end
end end
describe "GET /api/pleroma/admin/chats/:id" do describe "GET /api/pleroma/admin/chats/:id" do
setup do: admin_setup() setup do
clear_config([:instance, :admin_privileges], [:messages_read])
admin_setup()
end
test "it returns a chat", %{conn: conn} do test "it returns a chat", %{conn: conn} do
user = insert(:user) user = insert(:user)
@ -135,6 +161,14 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do
assert %{} = result["receiver"] assert %{} = result["receiver"]
refute result["account"] refute result["account"]
end end
test "it requires privileged role :messages_read", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn = get(conn, "/api/pleroma/admin/chats/some_id")
assert json_response(conn, :forbidden)
end
end end
describe "unauthorized chat moderation" do describe "unauthorized chat moderation" do

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.InstanceControllerTest do defmodule Pleroma.Web.AdminAPI.InstanceControllerTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase, async: false
use Oban.Testing, repo: Pleroma.Repo use Oban.Testing, repo: Pleroma.Repo
import Pleroma.Factory import Pleroma.Factory
@ -31,6 +31,7 @@ defmodule Pleroma.Web.AdminAPI.InstanceControllerTest do
end end
test "GET /instances/:instance/statuses", %{conn: conn} do test "GET /instances/:instance/statuses", %{conn: conn} do
clear_config([:instance, :admin_privileges], [:messages_read])
user = insert(:user, local: false, ap_id: "https://archae.me/users/archaeme") user = insert(:user, local: false, ap_id: "https://archae.me/users/archaeme")
user2 = insert(:user, local: false, ap_id: "https://test.com/users/test") user2 = insert(:user, local: false, ap_id: "https://test.com/users/test")
insert_pair(:note_activity, user: user) insert_pair(:note_activity, user: user)
@ -60,9 +61,14 @@ defmodule Pleroma.Web.AdminAPI.InstanceControllerTest do
|> json_response(200) |> json_response(200)
assert length(activities) == 3 assert length(activities) == 3
clear_config([:instance, :admin_privileges], [])
conn |> get("/api/pleroma/admin/instances/archae.me/statuses") |> json_response(:forbidden)
end end
test "DELETE /instances/:instance", %{conn: conn} do test "DELETE /instances/:instance", %{conn: conn} do
clear_config([:instance, :admin_privileges], [:instances_delete])
user = insert(:user, nickname: "lain@lain.com") user = insert(:user, nickname: "lain@lain.com")
post = insert(:note_activity, user: user) post = insert(:note_activity, user: user)
@ -76,5 +82,11 @@ defmodule Pleroma.Web.AdminAPI.InstanceControllerTest do
assert response == "lain.com" assert response == "lain.com"
refute Repo.reload(user).is_active refute Repo.reload(user).is_active
refute Repo.reload(post) refute Repo.reload(post)
clear_config([:instance, :admin_privileges], [])
conn
|> delete("/api/pleroma/admin/instances/lain.com")
|> json_response(:forbidden)
end end
end end

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.InviteControllerTest do defmodule Pleroma.Web.AdminAPI.InviteControllerTest do
use Pleroma.Web.ConnCase, async: true use Pleroma.Web.ConnCase, async: false
import Pleroma.Factory import Pleroma.Factory
@ -23,8 +23,25 @@ defmodule Pleroma.Web.AdminAPI.InviteControllerTest do
end end
describe "POST /api/pleroma/admin/users/email_invite, with valid config" do describe "POST /api/pleroma/admin/users/email_invite, with valid config" do
setup do: clear_config([:instance, :registrations_open], false) setup do
setup do: clear_config([:instance, :invites_enabled], true) clear_config([:instance, :registrations_open], false)
clear_config([:instance, :invites_enabled], true)
clear_config([:instance, :admin_privileges], [:users_manage_invites])
end
test "returns 403 if not privileged with :users_manage_invites", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn =
conn
|> put_req_header("content-type", "application/json;charset=utf-8")
|> post("/api/pleroma/admin/users/email_invite", %{
email: "foo@bar.com",
name: "J. D."
})
assert json_response(conn, :forbidden)
end
test "sends invitation and returns 204", %{admin: admin, conn: conn} do test "sends invitation and returns 204", %{admin: admin, conn: conn} do
recipient_email = "foo@bar.com" recipient_email = "foo@bar.com"
@ -114,8 +131,11 @@ defmodule Pleroma.Web.AdminAPI.InviteControllerTest do
end end
describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do
setup do: clear_config([:instance, :registrations_open]) setup do
setup do: clear_config([:instance, :invites_enabled]) clear_config([:instance, :registrations_open])
clear_config([:instance, :invites_enabled])
clear_config([:instance, :admin_privileges], [:users_manage_invites])
end
test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do
clear_config([:instance, :registrations_open], false) clear_config([:instance, :registrations_open], false)
@ -157,6 +177,21 @@ defmodule Pleroma.Web.AdminAPI.InviteControllerTest do
end end
describe "POST /api/pleroma/admin/users/invite_token" do describe "POST /api/pleroma/admin/users/invite_token" do
setup do
clear_config([:instance, :admin_privileges], [:users_manage_invites])
end
test "returns 403 if not privileged with :users_manage_invites", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/users/invite_token")
assert json_response(conn, :forbidden)
end
test "without options", %{conn: conn} do test "without options", %{conn: conn} do
conn = conn =
conn conn
@ -221,6 +256,18 @@ defmodule Pleroma.Web.AdminAPI.InviteControllerTest do
end end
describe "GET /api/pleroma/admin/users/invites" do describe "GET /api/pleroma/admin/users/invites" do
setup do
clear_config([:instance, :admin_privileges], [:users_manage_invites])
end
test "returns 403 if not privileged with :users_manage_invites", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn = get(conn, "/api/pleroma/admin/users/invites")
assert json_response(conn, :forbidden)
end
test "no invites", %{conn: conn} do test "no invites", %{conn: conn} do
conn = get(conn, "/api/pleroma/admin/users/invites") conn = get(conn, "/api/pleroma/admin/users/invites")
@ -249,6 +296,21 @@ defmodule Pleroma.Web.AdminAPI.InviteControllerTest do
end end
describe "POST /api/pleroma/admin/users/revoke_invite" do describe "POST /api/pleroma/admin/users/revoke_invite" do
setup do
clear_config([:instance, :admin_privileges], [:users_manage_invites])
end
test "returns 403 if not privileged with :users_manage_invites", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})
assert json_response(conn, :forbidden)
end
test "with token", %{conn: conn} do test "with token", %{conn: conn} do
{:ok, invite} = UserInviteToken.create_invite() {:ok, invite} = UserInviteToken.create_invite()

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.ReportControllerTest do defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
use Pleroma.Web.ConnCase, async: true use Pleroma.Web.ConnCase, async: false
import Pleroma.Factory import Pleroma.Factory
@ -26,6 +26,20 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
end end
describe "GET /api/pleroma/admin/reports/:id" do describe "GET /api/pleroma/admin/reports/:id" do
setup do
clear_config([:instance, :admin_privileges], [:reports_manage_reports])
end
test "returns 403 if not privileged with :reports_manage_reports", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn =
conn
|> get("/api/pleroma/admin/reports/report_id")
assert json_response(conn, :forbidden)
end
test "returns report by its id", %{conn: conn} do test "returns report by its id", %{conn: conn} do
[reporter, target_user] = insert_pair(:user) [reporter, target_user] = insert_pair(:user)
activity = insert(:note_activity, user: target_user) activity = insert(:note_activity, user: target_user)
@ -89,6 +103,8 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
describe "PATCH /api/pleroma/admin/reports" do describe "PATCH /api/pleroma/admin/reports" do
setup do setup do
clear_config([:instance, :admin_privileges], [:reports_manage_reports])
[reporter, target_user] = insert_pair(:user) [reporter, target_user] = insert_pair(:user)
activity = insert(:note_activity, user: target_user) activity = insert(:note_activity, user: target_user)
@ -112,6 +128,24 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
} }
end end
test "returns 403 if not privileged with :reports_manage_reports", %{
conn: conn,
id: id,
admin: admin
} do
clear_config([:instance, :admin_privileges], [])
conn =
conn
|> assign(:token, insert(:oauth_token, user: admin, scopes: ["admin:write:reports"]))
|> put_req_header("content-type", "application/json")
|> patch("/api/pleroma/admin/reports", %{
"reports" => [%{"state" => "resolved", "id" => id}]
})
assert json_response(conn, :forbidden)
end
test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do
read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"]) read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"])
write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"]) write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"])
@ -235,6 +269,20 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
end end
describe "GET /api/pleroma/admin/reports" do describe "GET /api/pleroma/admin/reports" do
setup do
clear_config([:instance, :admin_privileges], [:reports_manage_reports])
end
test "returns 403 if not privileged with :reports_manage_reports", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn =
conn
|> get(report_path(conn, :index))
assert json_response(conn, :forbidden)
end
test "returns empty response when no reports created", %{conn: conn} do test "returns empty response when no reports created", %{conn: conn} do
response = response =
conn conn
@ -343,6 +391,8 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
describe "POST /api/pleroma/admin/reports/:id/notes" do describe "POST /api/pleroma/admin/reports/:id/notes" do
setup %{conn: conn, admin: admin} do setup %{conn: conn, admin: admin} do
clear_config([:instance, :admin_privileges], [:reports_manage_reports])
[reporter, target_user] = insert_pair(:user) [reporter, target_user] = insert_pair(:user)
activity = insert(:note_activity, user: target_user) activity = insert(:note_activity, user: target_user)
@ -371,6 +421,25 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
} }
end end
test "returns 403 if not privileged with :reports_manage_reports", %{
conn: conn,
report_id: report_id
} do
clear_config([:instance, :admin_privileges], [])
post_conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/reports/#{report_id}/notes", %{
content: "this is disgusting2!"
})
delete_conn = delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/note.id")
assert json_response(post_conn, :forbidden)
assert json_response(delete_conn, :forbidden)
end
test "it creates report note", %{admin_id: admin_id, report_id: report_id} do test "it creates report note", %{admin_id: admin_id, report_id: report_id} do
assert [note, _] = Repo.all(ReportNote) assert [note, _] = Repo.all(ReportNote)

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.StatusControllerTest do defmodule Pleroma.Web.AdminAPI.StatusControllerTest do
use Pleroma.Web.ConnCase, async: true use Pleroma.Web.ConnCase, async: false
import Pleroma.Factory import Pleroma.Factory
@ -26,6 +26,10 @@ defmodule Pleroma.Web.AdminAPI.StatusControllerTest do
end end
describe "GET /api/pleroma/admin/statuses/:id" do describe "GET /api/pleroma/admin/statuses/:id" do
setup do
clear_config([:instance, :admin_privileges], [:messages_read])
end
test "not found", %{conn: conn} do test "not found", %{conn: conn} do
assert conn assert conn
|> get("/api/pleroma/admin/statuses/not_found") |> get("/api/pleroma/admin/statuses/not_found")
@ -50,10 +54,17 @@ defmodule Pleroma.Web.AdminAPI.StatusControllerTest do
assert account["is_active"] == actor.is_active assert account["is_active"] == actor.is_active
assert account["is_confirmed"] == actor.is_confirmed assert account["is_confirmed"] == actor.is_confirmed
end end
test "denies reading activity when not privileged", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
assert conn |> get("/api/pleroma/admin/statuses/some_id") |> json_response(:forbidden)
end
end end
describe "PUT /api/pleroma/admin/statuses/:id" do describe "PUT /api/pleroma/admin/statuses/:id" do
setup do setup do
clear_config([:instance, :admin_privileges], [:messages_delete])
activity = insert(:note_activity) activity = insert(:note_activity)
%{id: activity.id} %{id: activity.id}
@ -122,10 +133,20 @@ defmodule Pleroma.Web.AdminAPI.StatusControllerTest do
assert %{"error" => "test - Invalid value for enum."} = assert %{"error" => "test - Invalid value for enum."} =
json_response_and_validate_schema(conn, :bad_request) json_response_and_validate_schema(conn, :bad_request)
end end
test "it requires privileged role :messages_delete", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
assert conn
|> put_req_header("content-type", "application/json")
|> put("/api/pleroma/admin/statuses/some_id", %{})
|> json_response(:forbidden)
end
end end
describe "DELETE /api/pleroma/admin/statuses/:id" do describe "DELETE /api/pleroma/admin/statuses/:id" do
setup do setup do
clear_config([:instance, :admin_privileges], [:messages_delete])
activity = insert(:note_activity) activity = insert(:note_activity)
%{id: activity.id} %{id: activity.id}
@ -149,9 +170,22 @@ defmodule Pleroma.Web.AdminAPI.StatusControllerTest do
assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"} assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"}
end end
test "it requires privileged role :messages_delete", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
assert conn
|> put_req_header("content-type", "application/json")
|> delete("/api/pleroma/admin/statuses/some_id")
|> json_response(:forbidden)
end
end end
describe "GET /api/pleroma/admin/statuses" do describe "GET /api/pleroma/admin/statuses" do
setup do
clear_config([:instance, :admin_privileges], [:messages_read])
end
test "returns all public and unlisted statuses", %{conn: conn, admin: admin} do test "returns all public and unlisted statuses", %{conn: conn, admin: admin} do
blocked = insert(:user) blocked = insert(:user)
user = insert(:user) user = insert(:user)
@ -197,5 +231,13 @@ defmodule Pleroma.Web.AdminAPI.StatusControllerTest do
conn = get(conn, "/api/pleroma/admin/statuses?godmode=true") conn = get(conn, "/api/pleroma/admin/statuses?godmode=true")
assert json_response_and_validate_schema(conn, 200) |> length() == 3 assert json_response_and_validate_schema(conn, 200) |> length() == 3
end end
test "it requires privileged role :messages_read", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn = get(conn, "/api/pleroma/admin/statuses")
assert json_response(conn, :forbidden)
end
end end
end end

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.UserControllerTest do defmodule Pleroma.Web.AdminAPI.UserControllerTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase, async: false
use Oban.Testing, repo: Pleroma.Repo use Oban.Testing, repo: Pleroma.Repo
import Mock import Mock
@ -38,6 +38,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
end end
test "with valid `admin_token` query parameter, skips OAuth scopes check" do test "with valid `admin_token` query parameter, skips OAuth scopes check" do
clear_config([:instance, :admin_privileges], [:users_read])
clear_config([:admin_token], "password123") clear_config([:admin_token], "password123")
user = insert(:user) user = insert(:user)
@ -47,53 +48,10 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
assert json_response_and_validate_schema(conn, 200) assert json_response_and_validate_schema(conn, 200)
end end
test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope",
%{admin: admin} do
user = insert(:user)
url = "/api/pleroma/admin/users/#{user.nickname}"
good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
bad_token3 = nil
for good_token <- [good_token1, good_token2, good_token3] do
conn =
build_conn()
|> assign(:user, admin)
|> assign(:token, good_token)
|> get(url)
assert json_response_and_validate_schema(conn, 200)
end
for good_token <- [good_token1, good_token2, good_token3] do
conn =
build_conn()
|> assign(:user, nil)
|> assign(:token, good_token)
|> get(url)
assert json_response(conn, :forbidden)
end
for bad_token <- [bad_token1, bad_token2, bad_token3] do
conn =
build_conn()
|> assign(:user, admin)
|> assign(:token, bad_token)
|> get(url)
assert json_response_and_validate_schema(conn, :forbidden)
end
end
describe "DELETE /api/pleroma/admin/users" do describe "DELETE /api/pleroma/admin/users" do
test "single user", %{admin: admin, conn: conn} do test "single user", %{admin: admin, conn: conn} do
clear_config([:instance, :federating], true) clear_config([:instance, :federating], true)
clear_config([:instance, :admin_privileges], [:users_delete])
user = user =
insert(:user, insert(:user,
@ -149,6 +107,8 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
end end
test "multiple users", %{admin: admin, conn: conn} do test "multiple users", %{admin: admin, conn: conn} do
clear_config([:instance, :admin_privileges], [:users_delete])
user_one = insert(:user) user_one = insert(:user)
user_two = insert(:user) user_two = insert(:user)
@ -168,6 +128,17 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
assert response -- [user_one.nickname, user_two.nickname] == [] assert response -- [user_one.nickname, user_two.nickname] == []
end end
test "Needs privileged role", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
response =
conn
|> put_req_header("accept", "application/json")
|> delete("/api/pleroma/admin/users?nickname=nickname")
assert json_response(response, :forbidden)
end
end end
describe "/api/pleroma/admin/users" do describe "/api/pleroma/admin/users" do
@ -307,7 +278,19 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
end end
end end
describe "/api/pleroma/admin/users/:nickname" do describe "GET /api/pleroma/admin/users/:nickname" do
setup do
clear_config([:instance, :admin_privileges], [:users_read])
end
test "returns 403 if not privileged with :users_read", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn = get(conn, "/api/pleroma/admin/users/user.nickname")
assert json_response(conn, :forbidden)
end
test "Show", %{conn: conn} do test "Show", %{conn: conn} do
user = insert(:user) user = insert(:user)
@ -323,6 +306,50 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
assert %{"error" => "Not found"} == json_response_and_validate_schema(conn, 404) assert %{"error" => "Not found"} == json_response_and_validate_schema(conn, 404)
end end
test "requires admin:read:accounts or broader scope",
%{admin: admin} do
user = insert(:user)
url = "/api/pleroma/admin/users/#{user.nickname}"
good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
bad_token3 = nil
for good_token <- [good_token1, good_token2, good_token3] do
conn =
build_conn()
|> assign(:user, admin)
|> assign(:token, good_token)
|> get(url)
assert json_response_and_validate_schema(conn, 200)
end
for good_token <- [good_token1, good_token2, good_token3] do
conn =
build_conn()
|> assign(:user, nil)
|> assign(:token, good_token)
|> get(url)
assert json_response(conn, :forbidden)
end
for bad_token <- [bad_token1, bad_token2, bad_token3] do
conn =
build_conn()
|> assign(:user, admin)
|> assign(:token, bad_token)
|> get(url)
assert json_response_and_validate_schema(conn, :forbidden)
end
end
end end
describe "/api/pleroma/admin/users/follow" do describe "/api/pleroma/admin/users/follow" do
@ -378,6 +405,18 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
end end
describe "GET /api/pleroma/admin/users" do describe "GET /api/pleroma/admin/users" do
setup do
clear_config([:instance, :admin_privileges], [:users_read])
end
test "returns 403 if not privileged with :users_read", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn = get(conn, "/api/pleroma/admin/users?page=1")
assert json_response(conn, :forbidden)
end
test "renders users array for the first page", %{conn: conn, admin: admin} do test "renders users array for the first page", %{conn: conn, admin: admin} do
user = insert(:user, local: false, tags: ["foo", "bar"]) user = insert(:user, local: false, tags: ["foo", "bar"])
user2 = insert(:user, is_approved: false, registration_reason: "I'm a chill dude") user2 = insert(:user, is_approved: false, registration_reason: "I'm a chill dude")
@ -810,49 +849,9 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
end end
end end
test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
user_one = insert(:user, is_active: false)
user_two = insert(:user, is_active: false)
conn =
conn
|> put_req_header("content-type", "application/json")
|> patch(
"/api/pleroma/admin/users/activate",
%{nicknames: [user_one.nickname, user_two.nickname]}
)
response = json_response_and_validate_schema(conn, 200)
assert Enum.map(response["users"], & &1["is_active"]) == [true, true]
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", %{admin: admin, conn: conn} do
user_one = insert(:user, is_active: true)
user_two = insert(:user, is_active: true)
conn =
conn
|> put_req_header("content-type", "application/json")
|> patch(
"/api/pleroma/admin/users/deactivate",
%{nicknames: [user_one.nickname, user_two.nickname]}
)
response = json_response_and_validate_schema(conn, 200)
assert Enum.map(response["users"], & &1["is_active"]) == [false, false]
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/approve", %{admin: admin, conn: conn} do test "PATCH /api/pleroma/admin/users/approve", %{admin: admin, conn: conn} do
clear_config([:instance, :admin_privileges], [:users_manage_invites])
user_one = insert(:user, is_approved: false) user_one = insert(:user, is_approved: false)
user_two = insert(:user, is_approved: false) user_two = insert(:user, is_approved: false)
@ -873,6 +872,21 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
"@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}" "@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}"
end end
test "PATCH /api/pleroma/admin/users/approve returns 403 if not privileged with :users_manage_invites",
%{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn =
conn
|> put_req_header("content-type", "application/json")
|> patch(
"/api/pleroma/admin/users/approve",
%{nicknames: ["user_one.nickname", "user_two.nickname"]}
)
assert json_response(conn, :forbidden)
end
test "PATCH /api/pleroma/admin/users/suggest", %{admin: admin, conn: conn} do test "PATCH /api/pleroma/admin/users/suggest", %{admin: admin, conn: conn} do
user1 = insert(:user, is_suggested: false) user1 = insert(:user, is_suggested: false)
user2 = insert(:user, is_suggested: false) user2 = insert(:user, is_suggested: false)
@ -923,24 +937,113 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
"@#{admin.nickname} removed suggested users: @#{user1.nickname}, @#{user2.nickname}" "@#{admin.nickname} removed suggested users: @#{user1.nickname}, @#{user2.nickname}"
end end
test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do describe "user activation" do
user = insert(:user) test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
clear_config([:instance, :admin_privileges], [:users_manage_activation_state])
conn = user_one = insert(:user, is_active: false)
conn user_two = insert(:user, is_active: false)
|> put_req_header("content-type", "application/json")
|> patch("/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
assert json_response_and_validate_schema(conn, 200) == conn =
user_response( conn
user, |> put_req_header("content-type", "application/json")
%{"is_active" => !user.is_active} |> patch(
) "/api/pleroma/admin/users/activate",
%{nicknames: [user_one.nickname, user_two.nickname]}
)
log_entry = Repo.one(ModerationLog) response = json_response_and_validate_schema(conn, 200)
assert Enum.map(response["users"], & &1["is_active"]) == [true, true]
assert ModerationLog.get_log_entry_message(log_entry) == log_entry = Repo.one(ModerationLog)
"@#{admin.nickname} deactivated users: @#{user.nickname}"
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", %{admin: admin, conn: conn} do
clear_config([:instance, :admin_privileges], [:users_manage_activation_state])
user_one = insert(:user, is_active: true)
user_two = insert(:user, is_active: true)
conn =
conn
|> put_req_header("content-type", "application/json")
|> patch(
"/api/pleroma/admin/users/deactivate",
%{nicknames: [user_one.nickname, user_two.nickname]}
)
response = json_response_and_validate_schema(conn, 200)
assert Enum.map(response["users"], & &1["is_active"]) == [false, false]
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", %{admin: admin, conn: conn} do
clear_config([:instance, :admin_privileges], [:users_manage_activation_state])
user = insert(:user)
conn =
conn
|> put_req_header("content-type", "application/json")
|> patch("/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
assert json_response_and_validate_schema(conn, 200) ==
user_response(
user,
%{"is_active" => !user.is_active}
)
log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} deactivated users: @#{user.nickname}"
end
test "it requires privileged role :statuses_activation to activate", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn =
conn
|> put_req_header("content-type", "application/json")
|> patch(
"/api/pleroma/admin/users/activate",
%{nicknames: ["user_one.nickname", "user_two.nickname"]}
)
assert json_response(conn, :forbidden)
end
test "it requires privileged role :statuses_activation to deactivate", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn =
conn
|> put_req_header("content-type", "application/json")
|> patch(
"/api/pleroma/admin/users/deactivate",
%{nicknames: ["user_one.nickname", "user_two.nickname"]}
)
assert json_response(conn, :forbidden)
end
test "it requires privileged role :statuses_activation to toggle activation", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn =
conn
|> put_req_header("content-type", "application/json")
|> patch("/api/pleroma/admin/users/user.nickname/toggle_activation")
assert json_response(conn, :forbidden)
end
end end
defp user_response(user, attrs \\ %{}) do defp user_response(user, attrs \\ %{}) do

View file

@ -4,7 +4,7 @@
defmodule Pleroma.Web.CommonAPITest do defmodule Pleroma.Web.CommonAPITest do
use Oban.Testing, repo: Pleroma.Repo use Oban.Testing, repo: Pleroma.Repo
use Pleroma.DataCase use Pleroma.DataCase, async: false
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Chat alias Pleroma.Chat
@ -331,7 +331,7 @@ defmodule Pleroma.Web.CommonAPITest do
refute Activity.get_by_id(post.id) refute Activity.get_by_id(post.id)
end end
test "it does not allow a user to delete their posts" do test "it does not allow a user to delete posts from another user" do
user = insert(:user) user = insert(:user)
other_user = insert(:user) other_user = insert(:user)
@ -341,7 +341,8 @@ defmodule Pleroma.Web.CommonAPITest do
assert Activity.get_by_id(post.id) assert Activity.get_by_id(post.id)
end end
test "it allows moderators to delete other user's posts" do test "it allows privileged users to delete other user's posts" do
clear_config([:instance, :moderator_privileges], [:messages_delete])
user = insert(:user) user = insert(:user)
moderator = insert(:user, is_moderator: true) moderator = insert(:user, is_moderator: true)
@ -353,19 +354,20 @@ defmodule Pleroma.Web.CommonAPITest do
refute Activity.get_by_id(post.id) refute Activity.get_by_id(post.id)
end end
test "it allows admins to delete other user's posts" do test "it doesn't allow unprivileged mods or admins to delete other user's posts" do
clear_config([:instance, :admin_privileges], [])
clear_config([:instance, :moderator_privileges], [])
user = insert(:user) user = insert(:user)
moderator = insert(:user, is_admin: true) moderator = insert(:user, is_moderator: true, is_admin: true)
{:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"}) {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
assert {:ok, delete} = CommonAPI.delete(post.id, moderator) assert {:error, "Could not delete"} = CommonAPI.delete(post.id, moderator)
assert delete.local assert Activity.get_by_id(post.id)
refute Activity.get_by_id(post.id)
end end
test "superusers deleting non-local posts won't federate the delete" do test "privileged users deleting non-local posts won't federate the delete" do
clear_config([:instance, :admin_privileges], [:messages_delete])
# This is the user of the ingested activity # This is the user of the ingested activity
_user = _user =
insert(:user, insert(:user,
@ -374,7 +376,7 @@ defmodule Pleroma.Web.CommonAPITest do
last_refreshed_at: NaiveDateTime.utc_now() last_refreshed_at: NaiveDateTime.utc_now()
) )
moderator = insert(:user, is_admin: true) admin = insert(:user, is_admin: true)
data = data =
File.read!("test/fixtures/mastodon-post-activity.json") File.read!("test/fixtures/mastodon-post-activity.json")
@ -384,7 +386,7 @@ defmodule Pleroma.Web.CommonAPITest do
with_mock Pleroma.Web.Federator, with_mock Pleroma.Web.Federator,
publish: fn _ -> nil end do publish: fn _ -> nil end do
assert {:ok, delete} = CommonAPI.delete(post.id, moderator) assert {:ok, delete} = CommonAPI.delete(post.id, admin)
assert delete.local assert delete.local
refute called(Pleroma.Web.Federator.publish(:_)) refute called(Pleroma.Web.Federator.publish(:_))
end end

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase, async: false
alias Pleroma.Notification alias Pleroma.Notification
alias Pleroma.Repo alias Pleroma.Repo
@ -74,12 +74,15 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
end end
test "by default, does not contain pleroma:report" do test "by default, does not contain pleroma:report" do
%{user: user, conn: conn} = oauth_access(["read:notifications"]) clear_config([:instance, :moderator_privileges], [:reports_manage_reports])
user = insert(:user)
other_user = insert(:user) other_user = insert(:user)
third_user = insert(:user) third_user = insert(:user)
user {:ok, user} = user |> User.admin_api_update(%{is_moderator: true})
|> User.admin_api_update(%{is_moderator: true})
%{conn: conn} = oauth_access(["read:notifications"], user: user)
{:ok, activity} = CommonAPI.post(other_user, %{status: "hey"}) {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
@ -101,6 +104,39 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
assert [_] = result assert [_] = result
end end
test "Pleroma:report is hidden for non-privileged users" do
clear_config([:instance, :moderator_privileges], [:reports_manage_reports])
user = insert(:user)
other_user = insert(:user)
third_user = insert(:user)
{:ok, user} = user |> User.admin_api_update(%{is_moderator: true})
%{conn: conn} = oauth_access(["read:notifications"], user: user)
{:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
{:ok, _report} =
CommonAPI.report(third_user, %{account_id: other_user.id, status_ids: [activity.id]})
result =
conn
|> get("/api/v1/notifications?include_types[]=pleroma:report")
|> json_response_and_validate_schema(200)
assert [_] = result
clear_config([:instance, :moderator_privileges], [])
result =
conn
|> get("/api/v1/notifications?include_types[]=pleroma:report")
|> json_response_and_validate_schema(200)
assert [] == result
end
test "excludes mentions from blockers when blockers_visible is false" do test "excludes mentions from blockers when blockers_visible is false" do
clear_config([:activitypub, :blockers_visible], false) clear_config([:activitypub, :blockers_visible], false)

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase, async: false
use Oban.Testing, repo: Pleroma.Repo use Oban.Testing, repo: Pleroma.Repo
alias Pleroma.Activity alias Pleroma.Activity
@ -971,25 +971,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
assert Activity.get_by_id(activity.id) == activity assert Activity.get_by_id(activity.id) == activity
end end
test "when you're an admin", %{conn: conn} do test "when you're privileged to", %{conn: conn} do
activity = insert(:note_activity) clear_config([:instance, :moderator_privileges], [:messages_delete])
user = insert(:user, is_admin: true)
res_conn =
conn
|> assign(:user, user)
|> assign(:token, insert(:oauth_token, user: user, scopes: ["write:statuses"]))
|> delete("/api/v1/statuses/#{activity.id}")
assert %{} = json_response_and_validate_schema(res_conn, 200)
assert ModerationLog |> Repo.one() |> ModerationLog.get_log_entry_message() ==
"@#{user.nickname} deleted status ##{activity.id}"
refute Activity.get_by_id(activity.id)
end
test "when you're a moderator", %{conn: conn} do
activity = insert(:note_activity) activity = insert(:note_activity)
user = insert(:user, is_moderator: true) user = insert(:user, is_moderator: true)

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.AccountViewTest do defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
use Pleroma.DataCase use Pleroma.DataCase, async: false
alias Pleroma.User alias Pleroma.User
alias Pleroma.UserRelationship alias Pleroma.UserRelationship
@ -84,6 +84,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
tags: [], tags: [],
is_admin: false, is_admin: false,
is_moderator: false, is_moderator: false,
privileges: [],
is_suggested: false, is_suggested: false,
hide_favorites: true, hide_favorites: true,
hide_followers: false, hide_followers: false,
@ -99,6 +100,147 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
assert expected == AccountView.render("show.json", %{user: user, skip_visibility_check: true}) assert expected == AccountView.render("show.json", %{user: user, skip_visibility_check: true})
end end
describe "roles and privileges" do
setup do
clear_config([:instance, :moderator_privileges], [:cofe, :only_moderator])
clear_config([:instance, :admin_privileges], [:cofe, :only_admin])
%{
user: insert(:user),
moderator: insert(:user, is_moderator: true),
admin: insert(:user, is_admin: true),
moderator_admin: insert(:user, is_moderator: true, is_admin: true),
user_no_show_roles: insert(:user, show_role: false),
moderator_admin_no_show_roles:
insert(:user, is_moderator: true, is_admin: true, show_role: false)
}
end
test "shows roles and privileges when show_role: true", %{
user: user,
moderator: moderator,
admin: admin,
moderator_admin: moderator_admin,
user_no_show_roles: user_no_show_roles,
moderator_admin_no_show_roles: moderator_admin_no_show_roles
} do
assert %{pleroma: %{is_moderator: false, is_admin: false}} =
AccountView.render("show.json", %{user: user, skip_visibility_check: true})
assert [] ==
AccountView.render("show.json", %{user: user, skip_visibility_check: true})[
:pleroma
][:privileges]
|> Enum.sort()
assert %{pleroma: %{is_moderator: true, is_admin: false}} =
AccountView.render("show.json", %{user: moderator, skip_visibility_check: true})
assert [:cofe, :only_moderator] ==
AccountView.render("show.json", %{user: moderator, skip_visibility_check: true})[
:pleroma
][:privileges]
|> Enum.sort()
assert %{pleroma: %{is_moderator: false, is_admin: true}} =
AccountView.render("show.json", %{user: admin, skip_visibility_check: true})
assert [:cofe, :only_admin] ==
AccountView.render("show.json", %{user: admin, skip_visibility_check: true})[
:pleroma
][:privileges]
|> Enum.sort()
assert %{pleroma: %{is_moderator: true, is_admin: true}} =
AccountView.render("show.json", %{
user: moderator_admin,
skip_visibility_check: true
})
assert [:cofe, :only_admin, :only_moderator] ==
AccountView.render("show.json", %{
user: moderator_admin,
skip_visibility_check: true
})[:pleroma][:privileges]
|> Enum.sort()
refute match?(
%{pleroma: %{is_moderator: _}},
AccountView.render("show.json", %{
user: user_no_show_roles,
skip_visibility_check: true
})
)
refute match?(
%{pleroma: %{is_admin: _}},
AccountView.render("show.json", %{
user: user_no_show_roles,
skip_visibility_check: true
})
)
refute match?(
%{pleroma: %{privileges: _}},
AccountView.render("show.json", %{
user: user_no_show_roles,
skip_visibility_check: true
})
)
refute match?(
%{pleroma: %{is_moderator: _}},
AccountView.render("show.json", %{
user: moderator_admin_no_show_roles,
skip_visibility_check: true
})
)
refute match?(
%{pleroma: %{is_admin: _}},
AccountView.render("show.json", %{
user: moderator_admin_no_show_roles,
skip_visibility_check: true
})
)
refute match?(
%{pleroma: %{privileges: _}},
AccountView.render("show.json", %{
user: moderator_admin_no_show_roles,
skip_visibility_check: true
})
)
end
test "shows roles and privileges when viewing own account, even when show_role: false", %{
user_no_show_roles: user_no_show_roles,
moderator_admin_no_show_roles: moderator_admin_no_show_roles
} do
assert %{pleroma: %{is_moderator: false, is_admin: false, privileges: []}} =
AccountView.render("show.json", %{
user: user_no_show_roles,
skip_visibility_check: true,
for: user_no_show_roles
})
assert %{
pleroma: %{
is_moderator: true,
is_admin: true,
privileges: privileges
}
} =
AccountView.render("show.json", %{
user: moderator_admin_no_show_roles,
skip_visibility_check: true,
for: moderator_admin_no_show_roles
})
assert [:cofe, :only_admin, :only_moderator] == privileges |> Enum.sort()
end
end
describe "favicon" do describe "favicon" do
setup do setup do
[user: insert(:user)] [user: insert(:user)]
@ -186,6 +328,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
tags: [], tags: [],
is_admin: false, is_admin: false,
is_moderator: false, is_moderator: false,
privileges: [],
is_suggested: false, is_suggested: false,
hide_favorites: true, hide_favorites: true,
hide_followers: false, hide_followers: false,
@ -214,8 +357,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
assert represented.url == "https://channels.tests.funkwhale.audio/channels/compositions" assert represented.url == "https://channels.tests.funkwhale.audio/channels/compositions"
end end
test "Represent a deactivated user for an admin" do test "Represent a deactivated user for a privileged user" do
admin = insert(:user, is_admin: true) clear_config([:instance, :moderator_privileges], [:users_manage_activation_state])
admin = insert(:user, is_moderator: true)
deactivated_user = insert(:user, is_active: false) deactivated_user = insert(:user, is_active: false)
represented = AccountView.render("show.json", %{user: deactivated_user, for: admin}) represented = AccountView.render("show.json", %{user: deactivated_user, for: admin})
assert represented[:pleroma][:deactivated] == true assert represented[:pleroma][:deactivated] == true

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
use Pleroma.DataCase use Pleroma.DataCase, async: false
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Chat alias Pleroma.Chat
@ -218,9 +218,11 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
end end
test "Report notification" do test "Report notification" do
clear_config([:instance, :moderator_privileges], [:reports_manage_reports])
reporting_user = insert(:user) reporting_user = insert(:user)
reported_user = insert(:user) reported_user = insert(:user)
{:ok, moderator_user} = insert(:user) |> User.admin_api_update(%{is_moderator: true}) moderator_user = insert(:user, is_moderator: true)
{:ok, activity} = CommonAPI.report(reporting_user, %{account_id: reported_user.id}) {:ok, activity} = CommonAPI.report(reporting_user, %{account_id: reported_user.id})
{:ok, [notification]} = Notification.create_notifications(activity) {:ok, [notification]} = Notification.create_notifications(activity)

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.NodeInfoTest do defmodule Pleroma.Web.NodeInfoTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase, async: false
import Pleroma.Factory import Pleroma.Factory
@ -40,6 +40,19 @@ defmodule Pleroma.Web.NodeInfoTest do
assert admin.ap_id in result["metadata"]["staffAccounts"] assert admin.ap_id in result["metadata"]["staffAccounts"]
end end
test "nodeinfo shows roles and privileges", %{conn: conn} do
clear_config([:instance, :moderator_privileges], [:cofe])
clear_config([:instance, :admin_privileges], [:suya, :cofe])
conn =
conn
|> get("/nodeinfo/2.1.json")
assert result = json_response(conn, 200)
assert %{"admin" => ["suya", "cofe"], "moderator" => ["cofe"]} == result["metadata"]["roles"]
end
test "nodeinfo shows restricted nicknames", %{conn: conn} do test "nodeinfo shows restricted nicknames", %{conn: conn} do
conn = conn =
conn conn

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase, async: false
import Mock import Mock
import Tesla.Mock import Tesla.Mock
@ -30,6 +30,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do
describe "POST/PATCH/DELETE /api/pleroma/emoji/packs/files?name=:name" do describe "POST/PATCH/DELETE /api/pleroma/emoji/packs/files?name=:name" do
setup do setup do
clear_config([:instance, :admin_privileges], [:emoji_manage_emoji])
pack_file = "#{@emoji_path}/test_pack/pack.json" pack_file = "#{@emoji_path}/test_pack/pack.json"
original_content = File.read!(pack_file) original_content = File.read!(pack_file)
@ -377,5 +378,32 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do
}) })
|> json_response_and_validate_schema(:bad_request) |> json_response_and_validate_schema(:bad_request)
end end
test "it requires privileged role :emoji_manage_emoji", %{admin_conn: admin_conn} do
clear_config([:instance, :admin_privileges], [])
assert admin_conn
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/pleroma/emoji/packs/files?name=test_pack", %{
file: %Plug.Upload{
filename: "shortcode.png",
path: "#{Pleroma.Config.get([:instance, :static_dir])}/add/shortcode.png"
}
})
|> json_response(:forbidden)
assert admin_conn
|> put_req_header("content-type", "multipart/form-data")
|> patch("/api/pleroma/emoji/packs/files?name=test_pack", %{
shortcode: "blank",
new_filename: "dir_2/blank_3.png"
})
|> json_response(:forbidden)
assert admin_conn
|> put_req_header("content-type", "multipart/form-data")
|> delete("/api/pleroma/emoji/packs/files?name=test_pack&shortcode=blank3")
|> json_response(:forbidden)
end
end end
end end

View file

@ -99,6 +99,10 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do
end end
describe "GET /api/pleroma/emoji/packs/remote" do describe "GET /api/pleroma/emoji/packs/remote" do
setup do
clear_config([:instance, :admin_privileges], [:emoji_manage_emoji])
end
test "shareable instance", %{admin_conn: admin_conn, conn: conn} do test "shareable instance", %{admin_conn: admin_conn, conn: conn} do
resp = resp =
conn conn
@ -136,6 +140,14 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do
"error" => "The requested instance does not support sharing emoji packs" "error" => "The requested instance does not support sharing emoji packs"
} }
end end
test "it requires privileged role :emoji_manage_emoji", %{admin_conn: admin_conn} do
clear_config([:instance, :admin_privileges], [])
assert admin_conn
|> get("/api/pleroma/emoji/packs/remote?url=https://example.com")
|> json_response(:forbidden)
end
end end
describe "GET /api/pleroma/emoji/packs/archive?name=:name" do describe "GET /api/pleroma/emoji/packs/archive?name=:name" do
@ -170,6 +182,10 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do
end end
describe "POST /api/pleroma/emoji/packs/download" do describe "POST /api/pleroma/emoji/packs/download" do
setup do
clear_config([:instance, :admin_privileges], [:emoji_manage_emoji])
end
test "shared pack from remote and non shared from fallback-src", %{ test "shared pack from remote and non shared from fallback-src", %{
admin_conn: admin_conn, admin_conn: admin_conn,
conn: conn conn: conn
@ -344,10 +360,24 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do
"The pack was not set as shared and there is no fallback src to download from" "The pack was not set as shared and there is no fallback src to download from"
} }
end end
test "it requires privileged role :emoji_manage_emoji", %{admin_conn: conn} do
clear_config([:instance, :admin_privileges], [])
assert conn
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/pleroma/emoji/packs/download", %{
url: "https://example.com",
name: "test_pack",
as: "test_pack2"
})
|> json_response(:forbidden)
end
end end
describe "PATCH/update /api/pleroma/emoji/pack?name=:name" do describe "PATCH/update /api/pleroma/emoji/pack?name=:name" do
setup do setup do
clear_config([:instance, :admin_privileges], [:emoji_manage_emoji])
pack_file = "#{@emoji_path}/test_pack/pack.json" pack_file = "#{@emoji_path}/test_pack/pack.json"
original_content = File.read!(pack_file) original_content = File.read!(pack_file)
@ -435,9 +465,25 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do
"error" => "The fallback archive does not have all files specified in pack.json" "error" => "The fallback archive does not have all files specified in pack.json"
} }
end end
test "it requires privileged role :emoji_manage_emoji", %{
admin_conn: conn,
new_data: new_data
} do
clear_config([:instance, :admin_privileges], [])
assert conn
|> put_req_header("content-type", "multipart/form-data")
|> patch("/api/pleroma/emoji/pack?name=test_pack", %{metadata: new_data})
|> json_response(:forbidden)
end
end end
describe "POST/DELETE /api/pleroma/emoji/pack?name=:name" do describe "POST/DELETE /api/pleroma/emoji/pack?name=:name" do
setup do
clear_config([:instance, :admin_privileges], [:emoji_manage_emoji])
end
test "returns an error on creates pack when file system not writable", %{ test "returns an error on creates pack when file system not writable", %{
admin_conn: admin_conn admin_conn: admin_conn
} do } do
@ -520,6 +566,18 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do
"error" => "pack name cannot be empty" "error" => "pack name cannot be empty"
} }
end end
test "it requires privileged role :emoji_manage_emoji", %{admin_conn: admin_conn} do
clear_config([:instance, :admin_privileges], [])
assert admin_conn
|> post("/api/pleroma/emoji/pack?name= ")
|> json_response(:forbidden)
assert admin_conn
|> delete("/api/pleroma/emoji/pack?name= ")
|> json_response(:forbidden)
end
end end
test "deleting nonexisting pack", %{admin_conn: admin_conn} do test "deleting nonexisting pack", %{admin_conn: admin_conn} do
@ -578,6 +636,12 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do
"blank2" => "blank.png", "blank2" => "blank.png",
"foo" => "blank.png" "foo" => "blank.png"
} }
clear_config([:instance, :admin_privileges], [])
assert admin_conn
|> get("/api/pleroma/emoji/packs/import")
|> json_response(:forbidden)
end end
describe "GET /api/pleroma/emoji/pack?name=:name" do describe "GET /api/pleroma/emoji/pack?name=:name" do

View file

@ -0,0 +1,96 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.EnsurePrivilegedPlugTest do
use Pleroma.Web.ConnCase, async: true
alias Pleroma.Web.Plugs.EnsurePrivilegedPlug
import Pleroma.Factory
test "denies a user that isn't moderator or admin" do
clear_config([:instance, :admin_privileges], [])
user = insert(:user)
conn =
build_conn()
|> assign(:user, user)
|> EnsurePrivilegedPlug.call(:cofe)
assert conn.status == 403
end
test "accepts an admin that is privileged" do
clear_config([:instance, :admin_privileges], [:cofe])
user = insert(:user, is_admin: true)
conn = assign(build_conn(), :user, user)
ret_conn = EnsurePrivilegedPlug.call(conn, :cofe)
assert conn == ret_conn
end
test "denies an admin that isn't privileged" do
clear_config([:instance, :admin_privileges], [:suya])
user = insert(:user, is_admin: true)
conn =
build_conn()
|> assign(:user, user)
|> EnsurePrivilegedPlug.call(:cofe)
assert conn.status == 403
end
test "accepts a moderator that is privileged" do
clear_config([:instance, :moderator_privileges], [:cofe])
user = insert(:user, is_moderator: true)
conn = assign(build_conn(), :user, user)
ret_conn = EnsurePrivilegedPlug.call(conn, :cofe)
assert conn == ret_conn
end
test "denies a moderator that isn't privileged" do
clear_config([:instance, :moderator_privileges], [:suya])
user = insert(:user, is_moderator: true)
conn =
build_conn()
|> assign(:user, user)
|> EnsurePrivilegedPlug.call(:cofe)
assert conn.status == 403
end
test "accepts for a privileged role even if other role isn't privileged" do
clear_config([:instance, :admin_privileges], [:cofe])
clear_config([:instance, :moderator_privileges], [])
user = insert(:user, is_admin: true, is_moderator: true)
conn = assign(build_conn(), :user, user)
ret_conn = EnsurePrivilegedPlug.call(conn, :cofe)
# privileged through admin role
assert conn == ret_conn
clear_config([:instance, :admin_privileges], [])
clear_config([:instance, :moderator_privileges], [:cofe])
user = insert(:user, is_admin: true, is_moderator: true)
conn = assign(build_conn(), :user, user)
ret_conn = EnsurePrivilegedPlug.call(conn, :cofe)
# privileged through moderator role
assert conn == ret_conn
end
test "denies when no user is set" do
conn =
build_conn()
|> EnsurePrivilegedPlug.call(:cofe)
assert conn.status == 403
end
end

View file

@ -1,60 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.EnsureStaffPrivilegedPlugTest do
use Pleroma.Web.ConnCase, async: true
alias Pleroma.Web.Plugs.EnsureStaffPrivilegedPlug
import Pleroma.Factory
test "accepts a user that is an admin" do
user = insert(:user, is_admin: true)
conn = assign(build_conn(), :user, user)
ret_conn = EnsureStaffPrivilegedPlug.call(conn, %{})
assert conn == ret_conn
end
test "accepts a user that is a moderator when :privileged_staff is enabled" do
clear_config([:instance, :privileged_staff], true)
user = insert(:user, is_moderator: true)
conn = assign(build_conn(), :user, user)
ret_conn = EnsureStaffPrivilegedPlug.call(conn, %{})
assert conn == ret_conn
end
test "denies a user that is a moderator when :privileged_staff is disabled" do
clear_config([:instance, :privileged_staff], false)
user = insert(:user, is_moderator: true)
conn =
build_conn()
|> assign(:user, user)
|> EnsureStaffPrivilegedPlug.call(%{})
assert conn.status == 403
end
test "denies a user that isn't a staff member" do
user = insert(:user)
conn =
build_conn()
|> assign(:user, user)
|> EnsureStaffPrivilegedPlug.call(%{})
assert conn.status == 403
end
test "denies when a user isn't set" do
conn = EnsureStaffPrivilegedPlug.call(build_conn(), %{})
assert conn.status == 403
end
end