Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop
This commit is contained in:
commit
97f67e0786
28 changed files with 526 additions and 72 deletions
|
@ -50,6 +50,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Support for `X-Forwarded-For` and similar HTTP headers which used by reverse proxies to pass a real user IP address to the backend. Must not be enabled unless your instance is behind at least one reverse proxy (such as Nginx, Apache HTTPD or Varnish Cache).
|
- Support for `X-Forwarded-For` and similar HTTP headers which used by reverse proxies to pass a real user IP address to the backend. Must not be enabled unless your instance is behind at least one reverse proxy (such as Nginx, Apache HTTPD or Varnish Cache).
|
||||||
- MRF: New module which handles incoming posts based on their age. By default, all incoming posts that are older than 2 days will be unlisted and not shown to their followers.
|
- MRF: New module which handles incoming posts based on their age. By default, all incoming posts that are older than 2 days will be unlisted and not shown to their followers.
|
||||||
- User notification settings: Add `privacy_option` option.
|
- User notification settings: Add `privacy_option` option.
|
||||||
|
- User settings: Add _This account is a_ option.
|
||||||
|
- OAuth: admin scopes support (relevant setting: `[:auth, :enforce_oauth_admin_scope_usage]`).
|
||||||
<details>
|
<details>
|
||||||
<summary>API Changes</summary>
|
<summary>API Changes</summary>
|
||||||
|
|
||||||
|
@ -78,6 +80,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Pleroma API: Add Emoji reactions
|
- Pleroma API: Add Emoji reactions
|
||||||
- Admin API: Add `/api/pleroma/admin/instances/:instance/statuses` - lists all statuses from a given instance
|
- Admin API: Add `/api/pleroma/admin/instances/:instance/statuses` - lists all statuses from a given instance
|
||||||
- Admin API: `PATCH /api/pleroma/users/confirm_email` to confirm email for multiple users, `PATCH /api/pleroma/users/resend_confirmation_email` to resend confirmation email for multiple users
|
- Admin API: `PATCH /api/pleroma/users/confirm_email` to confirm email for multiple users, `PATCH /api/pleroma/users/resend_confirmation_email` to resend confirmation email for multiple users
|
||||||
|
- ActivityPub: Configurable `type` field of the actors.
|
||||||
|
- Mastodon API: `/api/v1/accounts/:id` has `source/pleroma/actor_type` field.
|
||||||
|
- Mastodon API: `/api/v1/update_credentials` accepts `actor_type` field.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -563,7 +563,10 @@
|
||||||
base_path: "/oauth",
|
base_path: "/oauth",
|
||||||
providers: ueberauth_providers
|
providers: ueberauth_providers
|
||||||
|
|
||||||
config :pleroma, :auth, oauth_consumer_strategies: oauth_consumer_strategies
|
config :pleroma,
|
||||||
|
:auth,
|
||||||
|
enforce_oauth_admin_scope_usage: false,
|
||||||
|
oauth_consumer_strategies: oauth_consumer_strategies
|
||||||
|
|
||||||
config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Sendmail, enabled: false
|
config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Sendmail, enabled: false
|
||||||
|
|
||||||
|
|
|
@ -2094,6 +2094,15 @@
|
||||||
type: :group,
|
type: :group,
|
||||||
description: "Authentication / authorization settings",
|
description: "Authentication / authorization settings",
|
||||||
children: [
|
children: [
|
||||||
|
%{
|
||||||
|
key: :enforce_oauth_admin_scope_usage,
|
||||||
|
type: :boolean,
|
||||||
|
description:
|
||||||
|
"OAuth admin scope requirement toggle. " <>
|
||||||
|
"If `true`, admin actions explicitly demand admin OAuth scope(s) presence in OAuth token " <>
|
||||||
|
"(client app must support admin scopes). If `false` and token doesn't have admin scope(s)," <>
|
||||||
|
"`is_admin` user flag grants access to admin-specific actions."
|
||||||
|
},
|
||||||
%{
|
%{
|
||||||
key: :auth_template,
|
key: :auth_template,
|
||||||
type: :string,
|
type: :string,
|
||||||
|
|
|
@ -2,6 +2,13 @@
|
||||||
|
|
||||||
Authentication is required and the user must be an admin.
|
Authentication is required and the user must be an admin.
|
||||||
|
|
||||||
|
Configuration options:
|
||||||
|
|
||||||
|
* `[:auth, :enforce_oauth_admin_scope_usage]` — OAuth admin scope requirement toggle.
|
||||||
|
If `true`, admin actions explicitly demand admin OAuth scope(s) presence in OAuth token (client app must support admin scopes).
|
||||||
|
If `false` and token doesn't have admin scope(s), `is_admin` user flag grants access to admin-specific actions.
|
||||||
|
Note that client app needs to explicitly support admin scopes and request them when obtaining auth token.
|
||||||
|
|
||||||
## `GET /api/pleroma/admin/users`
|
## `GET /api/pleroma/admin/users`
|
||||||
|
|
||||||
### List users
|
### List users
|
||||||
|
|
|
@ -66,6 +66,8 @@ Has these additional fields under the `pleroma` object:
|
||||||
|
|
||||||
- `show_role`: boolean, nullable, true when the user wants his role (e.g admin, moderator) to be shown
|
- `show_role`: boolean, nullable, true when the user wants his role (e.g admin, moderator) to be shown
|
||||||
- `no_rich_text` - boolean, nullable, true when html tags are stripped from all statuses requested from the API
|
- `no_rich_text` - boolean, nullable, true when html tags are stripped from all statuses requested from the API
|
||||||
|
- `discoverable`: boolean, true when the user allows discovery of the account in search results and other services.
|
||||||
|
- `actor_type`: string, the type of this account.
|
||||||
|
|
||||||
## Conversations
|
## Conversations
|
||||||
|
|
||||||
|
@ -146,6 +148,8 @@ Additional parameters can be added to the JSON body/Form data:
|
||||||
- `skip_thread_containment` - if true, skip filtering out broken threads
|
- `skip_thread_containment` - if true, skip filtering out broken threads
|
||||||
- `allow_following_move` - if true, allows automatically follow moved following accounts
|
- `allow_following_move` - if true, allows automatically follow moved following accounts
|
||||||
- `pleroma_background_image` - sets the background image of the user.
|
- `pleroma_background_image` - sets the background image of the user.
|
||||||
|
- `discoverable` - if true, discovery of this account in search results and other services is allowed.
|
||||||
|
- `actor_type` - the type of this account.
|
||||||
|
|
||||||
### Pleroma Settings Store
|
### Pleroma Settings Store
|
||||||
Pleroma has mechanism that allows frontends to save blobs of json for each user on the backend. This can be used to save frontend-specific settings for a user that the backend does not need to know about.
|
Pleroma has mechanism that allows frontends to save blobs of json for each user on the backend. This can be used to save frontend-specific settings for a user that the backend does not need to know about.
|
||||||
|
|
|
@ -52,7 +52,9 @@ def run(["migrate_from_db", env, delete?]) do
|
||||||
|> Enum.each(fn config ->
|
|> Enum.each(fn config ->
|
||||||
IO.write(
|
IO.write(
|
||||||
file,
|
file,
|
||||||
"config :#{config.group}, #{config.key}, #{inspect(Config.from_binary(config.value))}\r\n\r\n"
|
"config :#{config.group}, #{config.key}, #{
|
||||||
|
inspect(Config.from_binary(config.value), limit: :infinity)
|
||||||
|
}\r\n\r\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
if delete? do
|
if delete? do
|
||||||
|
|
|
@ -8,7 +8,6 @@ defmodule Mix.Tasks.Pleroma.User do
|
||||||
alias Ecto.Changeset
|
alias Ecto.Changeset
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.UserInviteToken
|
alias Pleroma.UserInviteToken
|
||||||
alias Pleroma.Web.OAuth
|
|
||||||
|
|
||||||
@shortdoc "Manages Pleroma users"
|
@shortdoc "Manages Pleroma users"
|
||||||
@moduledoc File.read!("docs/administration/CLI_tasks/user.md")
|
@moduledoc File.read!("docs/administration/CLI_tasks/user.md")
|
||||||
|
@ -383,8 +382,7 @@ def run(["sign_out", nickname]) do
|
||||||
start_pleroma()
|
start_pleroma()
|
||||||
|
|
||||||
with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
|
with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
|
||||||
OAuth.Token.delete_user_tokens(user)
|
User.global_sign_out(user)
|
||||||
OAuth.Authorization.delete_user_authorizations(user)
|
|
||||||
|
|
||||||
shell_info("#{nickname} signed out from all apps.")
|
shell_info("#{nickname} signed out from all apps.")
|
||||||
else
|
else
|
||||||
|
@ -422,10 +420,7 @@ defp set_moderator(user, value) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp set_admin(user, value) do
|
defp set_admin(user, value) do
|
||||||
{:ok, user} =
|
{:ok, user} = User.admin_api_update(user, %{is_admin: value})
|
||||||
user
|
|
||||||
|> Changeset.change(%{is_admin: value})
|
|
||||||
|> User.update_and_set_cache()
|
|
||||||
|
|
||||||
shell_info("Admin status of #{user.nickname}: #{user.is_admin}")
|
shell_info("Admin status of #{user.nickname}: #{user.is_admin}")
|
||||||
user
|
user
|
||||||
|
|
|
@ -65,4 +65,16 @@ def delete(key) do
|
||||||
def oauth_consumer_strategies, do: get([:auth, :oauth_consumer_strategies], [])
|
def oauth_consumer_strategies, do: get([:auth, :oauth_consumer_strategies], [])
|
||||||
|
|
||||||
def oauth_consumer_enabled?, do: oauth_consumer_strategies() != []
|
def oauth_consumer_enabled?, do: oauth_consumer_strategies() != []
|
||||||
|
|
||||||
|
def enforce_oauth_admin_scope_usage?, do: !!get([:auth, :enforce_oauth_admin_scope_usage])
|
||||||
|
|
||||||
|
def oauth_admin_scopes(scopes) when is_list(scopes) do
|
||||||
|
Enum.flat_map(
|
||||||
|
scopes,
|
||||||
|
fn scope ->
|
||||||
|
["admin:#{scope}"] ++
|
||||||
|
if enforce_oauth_admin_scope_usage?(), do: [], else: [scope]
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
import Pleroma.Web.Gettext
|
import Pleroma.Web.Gettext
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||||
|
|
||||||
@behaviour Plug
|
@behaviour Plug
|
||||||
|
@ -15,6 +16,8 @@ def init(%{scopes: _} = options), do: options
|
||||||
def call(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do
|
def call(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do
|
||||||
op = options[:op] || :|
|
op = options[:op] || :|
|
||||||
token = assigns[:token]
|
token = assigns[:token]
|
||||||
|
|
||||||
|
scopes = transform_scopes(scopes, options)
|
||||||
matched_scopes = token && filter_descendants(scopes, token.scopes)
|
matched_scopes = token && filter_descendants(scopes, token.scopes)
|
||||||
|
|
||||||
cond do
|
cond do
|
||||||
|
@ -60,6 +63,15 @@ def filter_descendants(scopes, supported_scopes) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc "Transforms scopes by applying supported options (e.g. :admin)"
|
||||||
|
def transform_scopes(scopes, options) do
|
||||||
|
if options[:admin] do
|
||||||
|
Config.oauth_admin_scopes(scopes)
|
||||||
|
else
|
||||||
|
scopes
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp maybe_perform_instance_privacy_check(%Plug.Conn{} = conn, options) do
|
defp maybe_perform_instance_privacy_check(%Plug.Conn{} = conn, options) do
|
||||||
if options[:skip_instance_privacy_check] do
|
if options[:skip_instance_privacy_check] do
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -5,19 +5,38 @@
|
||||||
defmodule Pleroma.Plugs.UserIsAdminPlug do
|
defmodule Pleroma.Plugs.UserIsAdminPlug do
|
||||||
import Pleroma.Web.TranslationHelpers
|
import Pleroma.Web.TranslationHelpers
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.OAuth
|
||||||
|
|
||||||
def init(options) do
|
def init(options) do
|
||||||
options
|
options
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(%{assigns: %{user: %User{is_admin: true}}} = conn, _) do
|
def call(%{assigns: %{user: %User{is_admin: true}} = assigns} = conn, _) do
|
||||||
|
token = assigns[:token]
|
||||||
|
|
||||||
|
cond do
|
||||||
|
not Pleroma.Config.enforce_oauth_admin_scope_usage?() ->
|
||||||
conn
|
conn
|
||||||
|
|
||||||
|
token && OAuth.Scopes.contains_admin_scopes?(token.scopes) ->
|
||||||
|
# Note: checking for _any_ admin scope presence, not necessarily fitting requested action.
|
||||||
|
# Thus, controller must explicitly invoke OAuthScopesPlug to verify scope requirements.
|
||||||
|
conn
|
||||||
|
|
||||||
|
true ->
|
||||||
|
fail(conn)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(conn, _) do
|
def call(conn, _) do
|
||||||
|
fail(conn)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fail(conn) do
|
||||||
conn
|
conn
|
||||||
|> render_error(:forbidden, "User is not admin.")
|
|> render_error(:forbidden, "User is not an admin or OAuth admin scope is not granted.")
|
||||||
|> halt
|
|> halt()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -127,6 +127,7 @@ defmodule Pleroma.User do
|
||||||
field(:invisible, :boolean, default: false)
|
field(:invisible, :boolean, default: false)
|
||||||
field(:allow_following_move, :boolean, default: true)
|
field(:allow_following_move, :boolean, default: true)
|
||||||
field(:skip_thread_containment, :boolean, default: false)
|
field(:skip_thread_containment, :boolean, default: false)
|
||||||
|
field(:actor_type, :string, default: "Person")
|
||||||
field(:also_known_as, {:array, :string}, default: [])
|
field(:also_known_as, {:array, :string}, default: [])
|
||||||
|
|
||||||
embeds_one(
|
embeds_one(
|
||||||
|
@ -346,6 +347,7 @@ def remote_user_creation(params) do
|
||||||
:following_count,
|
:following_count,
|
||||||
:discoverable,
|
:discoverable,
|
||||||
:invisible,
|
:invisible,
|
||||||
|
:actor_type,
|
||||||
:also_known_as
|
:also_known_as
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -396,6 +398,7 @@ def update_changeset(struct, params \\ %{}) do
|
||||||
:raw_fields,
|
:raw_fields,
|
||||||
:pleroma_settings_store,
|
:pleroma_settings_store,
|
||||||
:discoverable,
|
:discoverable,
|
||||||
|
:actor_type,
|
||||||
:also_known_as
|
:also_known_as
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -438,6 +441,7 @@ def upgrade_changeset(struct, params \\ %{}, remote? \\ false) do
|
||||||
:discoverable,
|
:discoverable,
|
||||||
:hide_followers_count,
|
:hide_followers_count,
|
||||||
:hide_follows_count,
|
:hide_follows_count,
|
||||||
|
:actor_type,
|
||||||
:also_known_as
|
:also_known_as
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -858,6 +862,13 @@ def get_friends(user, page \\ nil) do
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_friends_ap_ids(user) do
|
||||||
|
user
|
||||||
|
|> get_friends_query(nil)
|
||||||
|
|> select([u], u.ap_id)
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
|
|
||||||
def get_friends_ids(user, page \\ nil) do
|
def get_friends_ids(user, page \\ nil) do
|
||||||
user
|
user
|
||||||
|> get_friends_query(page)
|
|> get_friends_query(page)
|
||||||
|
@ -1132,7 +1143,8 @@ def muted_notifications?(%User{} = user, %User{} = target),
|
||||||
def blocks?(nil, _), do: false
|
def blocks?(nil, _), do: false
|
||||||
|
|
||||||
def blocks?(%User{} = user, %User{} = target) do
|
def blocks?(%User{} = user, %User{} = target) do
|
||||||
blocks_user?(user, target) || blocks_domain?(user, target)
|
blocks_user?(user, target) ||
|
||||||
|
(!User.following?(user, target) && blocks_domain?(user, target))
|
||||||
end
|
end
|
||||||
|
|
||||||
def blocks_user?(%User{} = user, %User{} = target) do
|
def blocks_user?(%User{} = user, %User{} = target) do
|
||||||
|
@ -1835,13 +1847,28 @@ defp truncate_field(%{"name" => name, "value" => value}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def admin_api_update(user, params) do
|
def admin_api_update(user, params) do
|
||||||
user
|
changeset =
|
||||||
|> cast(params, [
|
cast(user, params, [
|
||||||
:is_moderator,
|
:is_moderator,
|
||||||
:is_admin,
|
:is_admin,
|
||||||
:show_role
|
:show_role
|
||||||
])
|
])
|
||||||
|> update_and_set_cache()
|
|
||||||
|
with {:ok, updated_user} <- update_and_set_cache(changeset) do
|
||||||
|
if user.is_admin && !updated_user.is_admin do
|
||||||
|
# Tokens & authorizations containing any admin scopes must be revoked (revoking all).
|
||||||
|
# This is an extra safety measure (tokens' admin scopes won't be accepted for non-admins).
|
||||||
|
global_sign_out(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
{:ok, updated_user}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Signs user out of all applications"
|
||||||
|
def global_sign_out(user) do
|
||||||
|
OAuth.Authorization.delete_user_authorizations(user)
|
||||||
|
OAuth.Token.delete_user_tokens(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def mascot_update(user, url) do
|
def mascot_update(user, url) do
|
||||||
|
|
|
@ -950,6 +950,8 @@ defp restrict_blocked(query, %{"blocking_user" => %User{} = user} = opts) do
|
||||||
blocked_ap_ids = opts["blocked_users_ap_ids"] || User.blocked_users_ap_ids(user)
|
blocked_ap_ids = opts["blocked_users_ap_ids"] || User.blocked_users_ap_ids(user)
|
||||||
domain_blocks = user.domain_blocks || []
|
domain_blocks = user.domain_blocks || []
|
||||||
|
|
||||||
|
following_ap_ids = User.get_friends_ap_ids(user)
|
||||||
|
|
||||||
query =
|
query =
|
||||||
if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
|
if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
|
||||||
|
|
||||||
|
@ -964,8 +966,22 @@ defp restrict_blocked(query, %{"blocking_user" => %User{} = user} = opts) do
|
||||||
activity.data,
|
activity.data,
|
||||||
^blocked_ap_ids
|
^blocked_ap_ids
|
||||||
),
|
),
|
||||||
where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks),
|
where:
|
||||||
where: fragment("not (split_part(?->>'actor', '/', 3) = ANY(?))", o.data, ^domain_blocks)
|
fragment(
|
||||||
|
"(not (split_part(?, '/', 3) = ANY(?))) or ? = ANY(?)",
|
||||||
|
activity.actor,
|
||||||
|
^domain_blocks,
|
||||||
|
activity.actor,
|
||||||
|
^following_ap_ids
|
||||||
|
),
|
||||||
|
where:
|
||||||
|
fragment(
|
||||||
|
"(not (split_part(?->>'actor', '/', 3) = ANY(?))) or (?->>'actor') = ANY(?)",
|
||||||
|
o.data,
|
||||||
|
^domain_blocks,
|
||||||
|
o.data,
|
||||||
|
^following_ap_ids
|
||||||
|
)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1217,6 +1233,7 @@ defp object_to_user_data(data) do
|
||||||
data = Transmogrifier.maybe_fix_user_object(data)
|
data = Transmogrifier.maybe_fix_user_object(data)
|
||||||
discoverable = data["discoverable"] || false
|
discoverable = data["discoverable"] || false
|
||||||
invisible = data["invisible"] || false
|
invisible = data["invisible"] || false
|
||||||
|
actor_type = data["type"] || "Person"
|
||||||
|
|
||||||
user_data = %{
|
user_data = %{
|
||||||
ap_id: data["id"],
|
ap_id: data["id"],
|
||||||
|
@ -1232,6 +1249,7 @@ defp object_to_user_data(data) do
|
||||||
follower_address: data["followers"],
|
follower_address: data["followers"],
|
||||||
following_address: data["following"],
|
following_address: data["following"],
|
||||||
bio: data["summary"],
|
bio: data["summary"],
|
||||||
|
actor_type: actor_type,
|
||||||
also_known_as: Map.get(data, "alsoKnownAs", [])
|
also_known_as: Map.get(data, "alsoKnownAs", [])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ def render("user.json", %{user: user}) do
|
||||||
|
|
||||||
%{
|
%{
|
||||||
"id" => user.ap_id,
|
"id" => user.ap_id,
|
||||||
"type" => "Person",
|
"type" => user.actor_type,
|
||||||
"following" => "#{user.ap_id}/following",
|
"following" => "#{user.ap_id}/following",
|
||||||
"followers" => "#{user.ap_id}/followers",
|
"followers" => "#{user.ap_id}/followers",
|
||||||
"inbox" => "#{user.ap_id}/inbox",
|
"inbox" => "#{user.ap_id}/inbox",
|
||||||
|
|
|
@ -30,13 +30,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["read:accounts"]}
|
%{scopes: ["read:accounts"], admin: true}
|
||||||
when action in [:list_users, :user_show, :right_get, :invites]
|
when action in [:list_users, :user_show, :right_get, :invites]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["write:accounts"]}
|
%{scopes: ["write:accounts"], admin: true}
|
||||||
when action in [
|
when action in [
|
||||||
:get_invite_token,
|
:get_invite_token,
|
||||||
:revoke_invite,
|
:revoke_invite,
|
||||||
|
@ -58,35 +58,37 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["read:reports"]} when action in [:list_reports, :report_show]
|
%{scopes: ["read:reports"], admin: true}
|
||||||
|
when action in [:list_reports, :report_show]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["write:reports"]}
|
%{scopes: ["write:reports"], admin: true}
|
||||||
when action in [:report_update_state, :report_respond]
|
when action in [:report_update_state, :report_respond]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["read:statuses"]} when action == :list_user_statuses
|
%{scopes: ["read:statuses"], admin: true}
|
||||||
|
when action == :list_user_statuses
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["write:statuses"]}
|
%{scopes: ["write:statuses"], admin: true}
|
||||||
when action in [:status_update, :status_delete]
|
when action in [:status_update, :status_delete]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["read"]}
|
%{scopes: ["read"], admin: true}
|
||||||
when action in [:config_show, :migrate_to_db, :migrate_from_db, :list_log]
|
when action in [:config_show, :migrate_to_db, :migrate_from_db, :list_log]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["write"]}
|
%{scopes: ["write"], admin: true}
|
||||||
when action in [:relay_follow, :relay_unfollow, :config_update]
|
when action in [:relay_follow, :relay_unfollow, :config_update]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -188,6 +188,7 @@ def update_credentials(%{assigns: %{user: original_user}} = conn, params) do
|
||||||
{:ok, Map.merge(user.pleroma_settings_store, value)}
|
{:ok, Map.merge(user.pleroma_settings_store, value)}
|
||||||
end)
|
end)
|
||||||
|> add_if_present(params, "default_scope", :default_scope)
|
|> add_if_present(params, "default_scope", :default_scope)
|
||||||
|
|> add_if_present(params, "actor_type", :actor_type)
|
||||||
|
|
||||||
emojis_text = (user_params["display_name"] || "") <> (user_params["note"] || "")
|
emojis_text = (user_params["display_name"] || "") <> (user_params["note"] || "")
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
0
|
0
|
||||||
end
|
end
|
||||||
|
|
||||||
bot = (user.source_data["type"] || "Person") in ["Application", "Service"]
|
bot = user.actor_type in ["Application", "Service"]
|
||||||
|
|
||||||
emojis =
|
emojis =
|
||||||
(user.source_data["tag"] || [])
|
(user.source_data["tag"] || [])
|
||||||
|
@ -137,7 +137,8 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
fields: user.raw_fields,
|
fields: user.raw_fields,
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
discoverable: user.discoverable
|
discoverable: user.discoverable,
|
||||||
|
actor_type: user.actor_type
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -222,7 +222,7 @@ def token_exchange(
|
||||||
{:user_active, true} <- {:user_active, !user.deactivated},
|
{:user_active, true} <- {:user_active, !user.deactivated},
|
||||||
{:password_reset_pending, false} <-
|
{:password_reset_pending, false} <-
|
||||||
{:password_reset_pending, user.password_reset_pending},
|
{:password_reset_pending, user.password_reset_pending},
|
||||||
{:ok, scopes} <- validate_scopes(app, params),
|
{:ok, scopes} <- validate_scopes(app, params, user),
|
||||||
{:ok, auth} <- Authorization.create_authorization(app, user, scopes),
|
{:ok, auth} <- Authorization.create_authorization(app, user, scopes),
|
||||||
{:ok, token} <- Token.exchange_token(app, auth) do
|
{:ok, token} <- Token.exchange_token(app, auth) do
|
||||||
json(conn, Token.Response.build(user, token))
|
json(conn, Token.Response.build(user, token))
|
||||||
|
@ -471,7 +471,7 @@ defp do_create_authorization(
|
||||||
{:get_user, (user && {:ok, user}) || Authenticator.get_user(conn)},
|
{:get_user, (user && {:ok, user}) || Authenticator.get_user(conn)},
|
||||||
%App{} = app <- Repo.get_by(App, client_id: client_id),
|
%App{} = app <- Repo.get_by(App, client_id: client_id),
|
||||||
true <- redirect_uri in String.split(app.redirect_uris),
|
true <- redirect_uri in String.split(app.redirect_uris),
|
||||||
{:ok, scopes} <- validate_scopes(app, auth_attrs),
|
{:ok, scopes} <- validate_scopes(app, auth_attrs, user),
|
||||||
{:auth_active, true} <- {:auth_active, User.auth_active?(user)} do
|
{:auth_active, true} <- {:auth_active, User.auth_active?(user)} do
|
||||||
Authorization.create_authorization(app, user, scopes)
|
Authorization.create_authorization(app, user, scopes)
|
||||||
end
|
end
|
||||||
|
@ -487,12 +487,12 @@ defp get_session_registration_id(%Plug.Conn{} = conn), do: get_session(conn, :re
|
||||||
defp put_session_registration_id(%Plug.Conn{} = conn, registration_id),
|
defp put_session_registration_id(%Plug.Conn{} = conn, registration_id),
|
||||||
do: put_session(conn, :registration_id, registration_id)
|
do: put_session(conn, :registration_id, registration_id)
|
||||||
|
|
||||||
@spec validate_scopes(App.t(), map()) ::
|
@spec validate_scopes(App.t(), map(), User.t()) ::
|
||||||
{:ok, list()} | {:error, :missing_scopes | :unsupported_scopes}
|
{:ok, list()} | {:error, :missing_scopes | :unsupported_scopes}
|
||||||
defp validate_scopes(app, params) do
|
defp validate_scopes(%App{} = app, params, %User{} = user) do
|
||||||
params
|
params
|
||||||
|> Scopes.fetch_scopes(app.scopes)
|
|> Scopes.fetch_scopes(app.scopes)
|
||||||
|> Scopes.validate(app.scopes)
|
|> Scopes.validate(app.scopes, user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_redirect_uri(%App{} = app) do
|
def default_redirect_uri(%App{} = app) do
|
||||||
|
|
|
@ -7,6 +7,9 @@ defmodule Pleroma.Web.OAuth.Scopes do
|
||||||
Functions for dealing with scopes.
|
Functions for dealing with scopes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Fetch scopes from request params.
|
Fetch scopes from request params.
|
||||||
|
|
||||||
|
@ -53,15 +56,36 @@ def to_string(scopes), do: Enum.join(scopes, " ")
|
||||||
@doc """
|
@doc """
|
||||||
Validates scopes.
|
Validates scopes.
|
||||||
"""
|
"""
|
||||||
@spec validate(list() | nil, list()) ::
|
@spec validate(list() | nil, list(), User.t()) ::
|
||||||
{:ok, list()} | {:error, :missing_scopes | :unsupported_scopes}
|
{:ok, list()} | {:error, :missing_scopes | :unsupported_scopes}
|
||||||
def validate([], _app_scopes), do: {:error, :missing_scopes}
|
def validate(blank_scopes, _app_scopes, _user) when blank_scopes in [nil, []],
|
||||||
def validate(nil, _app_scopes), do: {:error, :missing_scopes}
|
do: {:error, :missing_scopes}
|
||||||
|
|
||||||
def validate(scopes, app_scopes) do
|
def validate(scopes, app_scopes, %User{} = user) do
|
||||||
case Pleroma.Plugs.OAuthScopesPlug.filter_descendants(scopes, app_scopes) do
|
with {:ok, _} <- ensure_scopes_support(scopes, app_scopes),
|
||||||
|
{:ok, scopes} <- authorize_admin_scopes(scopes, app_scopes, user) do
|
||||||
|
{:ok, scopes}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp ensure_scopes_support(scopes, app_scopes) do
|
||||||
|
case OAuthScopesPlug.filter_descendants(scopes, app_scopes) do
|
||||||
^scopes -> {:ok, scopes}
|
^scopes -> {:ok, scopes}
|
||||||
_ -> {:error, :unsupported_scopes}
|
_ -> {:error, :unsupported_scopes}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp authorize_admin_scopes(scopes, app_scopes, %User{} = user) do
|
||||||
|
if user.is_admin || !contains_admin_scopes?(scopes) || !contains_admin_scopes?(app_scopes) do
|
||||||
|
{:ok, scopes}
|
||||||
|
else
|
||||||
|
{:error, :unsupported_scopes}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def contains_admin_scopes?(scopes) do
|
||||||
|
scopes
|
||||||
|
|> OAuthScopesPlug.filter_descendants(["admin"])
|
||||||
|
|> Enum.any?()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["write"]}
|
%{scopes: ["write"], admin: true}
|
||||||
when action in [
|
when action in [
|
||||||
:create,
|
:create,
|
||||||
:delete,
|
:delete,
|
||||||
|
|
|
@ -528,7 +528,10 @@ defmodule Pleroma.Web.Router do
|
||||||
|
|
||||||
get("/users/:nickname/feed", Feed.FeedController, :feed)
|
get("/users/:nickname/feed", Feed.FeedController, :feed)
|
||||||
get("/users/:nickname", Feed.FeedController, :feed_redirect)
|
get("/users/:nickname", Feed.FeedController, :feed_redirect)
|
||||||
|
end
|
||||||
|
|
||||||
|
scope "/", Pleroma.Web do
|
||||||
|
pipe_through(:browser)
|
||||||
get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)
|
get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddActivitypubActorType do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table("users") do
|
||||||
|
add(:actor_type, :string, null: false, default: "Person")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -224,4 +224,42 @@ test "filters scopes which directly match or are ancestors of supported scopes"
|
||||||
assert f.(["admin:read"], ["write", "admin"]) == ["admin:read"]
|
assert f.(["admin:read"], ["write", "admin"]) == ["admin:read"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "transform_scopes/2" do
|
||||||
|
clear_config([:auth, :enforce_oauth_admin_scope_usage])
|
||||||
|
|
||||||
|
setup do
|
||||||
|
{:ok, %{f: &OAuthScopesPlug.transform_scopes/2}}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with :admin option, prefixes all requested scopes with `admin:` " <>
|
||||||
|
"and [optionally] keeps only prefixed scopes, " <>
|
||||||
|
"depending on `[:auth, :enforce_oauth_admin_scope_usage]` setting",
|
||||||
|
%{f: f} do
|
||||||
|
Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], false)
|
||||||
|
|
||||||
|
assert f.(["read"], %{admin: true}) == ["admin:read", "read"]
|
||||||
|
|
||||||
|
assert f.(["read", "write"], %{admin: true}) == [
|
||||||
|
"admin:read",
|
||||||
|
"read",
|
||||||
|
"admin:write",
|
||||||
|
"write"
|
||||||
|
]
|
||||||
|
|
||||||
|
Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], true)
|
||||||
|
|
||||||
|
assert f.(["read:accounts"], %{admin: true}) == ["admin:read:accounts"]
|
||||||
|
|
||||||
|
assert f.(["read", "write:reports"], %{admin: true}) == [
|
||||||
|
"admin:read",
|
||||||
|
"admin:write:reports"
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with no supported options, returns unmodified scopes", %{f: f} do
|
||||||
|
assert f.(["read"], %{}) == ["read"]
|
||||||
|
assert f.(["read", "write"], %{}) == ["read", "write"]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,21 +8,22 @@ defmodule Pleroma.Plugs.UserIsAdminPlugTest do
|
||||||
alias Pleroma.Plugs.UserIsAdminPlug
|
alias Pleroma.Plugs.UserIsAdminPlug
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
test "accepts a user that is admin" do
|
describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do
|
||||||
|
clear_config([:auth, :enforce_oauth_admin_scope_usage]) do
|
||||||
|
Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], false)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "accepts a user that is an admin" do
|
||||||
user = insert(:user, is_admin: true)
|
user = insert(:user, is_admin: true)
|
||||||
|
|
||||||
conn =
|
conn = assign(build_conn(), :user, user)
|
||||||
build_conn()
|
|
||||||
|> assign(:user, user)
|
|
||||||
|
|
||||||
ret_conn =
|
ret_conn = UserIsAdminPlug.call(conn, %{})
|
||||||
conn
|
|
||||||
|> UserIsAdminPlug.call(%{})
|
|
||||||
|
|
||||||
assert conn == ret_conn
|
assert conn == ret_conn
|
||||||
end
|
end
|
||||||
|
|
||||||
test "denies a user that isn't admin" do
|
test "denies a user that isn't an admin" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
|
@ -34,10 +35,89 @@ test "denies a user that isn't admin" do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "denies when a user isn't set" do
|
test "denies when a user isn't set" do
|
||||||
|
conn = UserIsAdminPlug.call(build_conn(), %{})
|
||||||
|
|
||||||
|
assert conn.status == 403
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "with [:auth, :enforce_oauth_admin_scope_usage]," do
|
||||||
|
clear_config([:auth, :enforce_oauth_admin_scope_usage]) do
|
||||||
|
Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], true)
|
||||||
|
end
|
||||||
|
|
||||||
|
setup do
|
||||||
|
admin_user = insert(:user, is_admin: true)
|
||||||
|
non_admin_user = insert(:user, is_admin: false)
|
||||||
|
blank_user = nil
|
||||||
|
|
||||||
|
{:ok, %{users: [admin_user, non_admin_user, blank_user]}}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "if token has any of admin scopes, accepts a user that is an admin", %{conn: conn} do
|
||||||
|
user = insert(:user, is_admin: true)
|
||||||
|
token = insert(:oauth_token, user: user, scopes: ["admin:something"])
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
build_conn()
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|
||||||
|
ret_conn = UserIsAdminPlug.call(conn, %{})
|
||||||
|
|
||||||
|
assert conn == ret_conn
|
||||||
|
end
|
||||||
|
|
||||||
|
test "if token has any of admin scopes, denies a user that isn't an admin", %{conn: conn} do
|
||||||
|
user = insert(:user, is_admin: false)
|
||||||
|
token = insert(:oauth_token, user: user, scopes: ["admin:something"])
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> assign(:token, token)
|
||||||
|> UserIsAdminPlug.call(%{})
|
|> UserIsAdminPlug.call(%{})
|
||||||
|
|
||||||
assert conn.status == 403
|
assert conn.status == 403
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "if token has any of admin scopes, denies when a user isn't set", %{conn: conn} do
|
||||||
|
token = insert(:oauth_token, scopes: ["admin:something"])
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, nil)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|> UserIsAdminPlug.call(%{})
|
||||||
|
|
||||||
|
assert conn.status == 403
|
||||||
|
end
|
||||||
|
|
||||||
|
test "if token lacks admin scopes, denies users regardless of is_admin flag",
|
||||||
|
%{users: users} do
|
||||||
|
for user <- users do
|
||||||
|
token = insert(:oauth_token, user: user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|> UserIsAdminPlug.call(%{})
|
||||||
|
|
||||||
|
assert conn.status == 403
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "if token is missing, denies users regardless of is_admin flag", %{users: users} do
|
||||||
|
for user <- users do
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> assign(:token, nil)
|
||||||
|
|> UserIsAdminPlug.call(%{})
|
||||||
|
|
||||||
|
assert conn.status == 403
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -63,4 +63,84 @@ test "settings are migrated to file and deleted from db", %{temp_file: temp_file
|
||||||
assert file =~ "config :pleroma, :setting_first,"
|
assert file =~ "config :pleroma, :setting_first,"
|
||||||
assert file =~ "config :pleroma, :setting_second,"
|
assert file =~ "config :pleroma, :setting_second,"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "load a settings with large values and pass to file", %{temp_file: temp_file} do
|
||||||
|
Config.create(%{
|
||||||
|
group: "pleroma",
|
||||||
|
key: ":instance",
|
||||||
|
value: [
|
||||||
|
name: "Pleroma",
|
||||||
|
email: "example@example.com",
|
||||||
|
notify_email: "noreply@example.com",
|
||||||
|
description: "A Pleroma instance, an alternative fediverse server",
|
||||||
|
limit: 5_000,
|
||||||
|
chat_limit: 5_000,
|
||||||
|
remote_limit: 100_000,
|
||||||
|
upload_limit: 16_000_000,
|
||||||
|
avatar_upload_limit: 2_000_000,
|
||||||
|
background_upload_limit: 4_000_000,
|
||||||
|
banner_upload_limit: 4_000_000,
|
||||||
|
poll_limits: %{
|
||||||
|
max_options: 20,
|
||||||
|
max_option_chars: 200,
|
||||||
|
min_expiration: 0,
|
||||||
|
max_expiration: 365 * 24 * 60 * 60
|
||||||
|
},
|
||||||
|
registrations_open: true,
|
||||||
|
federating: true,
|
||||||
|
federation_incoming_replies_max_depth: 100,
|
||||||
|
federation_reachability_timeout_days: 7,
|
||||||
|
federation_publisher_modules: [Pleroma.Web.ActivityPub.Publisher],
|
||||||
|
allow_relay: true,
|
||||||
|
rewrite_policy: Pleroma.Web.ActivityPub.MRF.NoOpPolicy,
|
||||||
|
public: true,
|
||||||
|
quarantined_instances: [],
|
||||||
|
managed_config: true,
|
||||||
|
static_dir: "instance/static/",
|
||||||
|
allowed_post_formats: ["text/plain", "text/html", "text/markdown", "text/bbcode"],
|
||||||
|
mrf_transparency: true,
|
||||||
|
mrf_transparency_exclusions: [],
|
||||||
|
autofollowed_nicknames: [],
|
||||||
|
max_pinned_statuses: 1,
|
||||||
|
no_attachment_links: true,
|
||||||
|
welcome_user_nickname: nil,
|
||||||
|
welcome_message: nil,
|
||||||
|
max_report_comment_size: 1000,
|
||||||
|
safe_dm_mentions: false,
|
||||||
|
healthcheck: false,
|
||||||
|
remote_post_retention_days: 90,
|
||||||
|
skip_thread_containment: true,
|
||||||
|
limit_to_local_content: :unauthenticated,
|
||||||
|
dynamic_configuration: false,
|
||||||
|
user_bio_length: 5000,
|
||||||
|
user_name_length: 100,
|
||||||
|
max_account_fields: 10,
|
||||||
|
max_remote_account_fields: 20,
|
||||||
|
account_field_name_length: 512,
|
||||||
|
account_field_value_length: 2048,
|
||||||
|
external_user_synchronization: true,
|
||||||
|
extended_nickname_format: true,
|
||||||
|
multi_factor_authentication: [
|
||||||
|
totp: [
|
||||||
|
# digits 6 or 8
|
||||||
|
digits: 6,
|
||||||
|
period: 30
|
||||||
|
],
|
||||||
|
backup_codes: [
|
||||||
|
number: 2,
|
||||||
|
length: 6
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
Mix.Tasks.Pleroma.Config.run(["migrate_from_db", "temp", "true"])
|
||||||
|
|
||||||
|
assert Repo.all(Config) == []
|
||||||
|
assert File.exists?(temp_file)
|
||||||
|
{:ok, file} = File.read(temp_file)
|
||||||
|
|
||||||
|
assert file ==
|
||||||
|
"use Mix.Config\n\nconfig :pleroma, :instance,\n name: \"Pleroma\",\n email: \"example@example.com\",\n notify_email: \"noreply@example.com\",\n description: \"A Pleroma instance, an alternative fediverse server\",\n limit: 5000,\n chat_limit: 5000,\n remote_limit: 100_000,\n upload_limit: 16_000_000,\n avatar_upload_limit: 2_000_000,\n background_upload_limit: 4_000_000,\n banner_upload_limit: 4_000_000,\n poll_limits: %{\n max_expiration: 31_536_000,\n max_option_chars: 200,\n max_options: 20,\n min_expiration: 0\n },\n registrations_open: true,\n federating: true,\n federation_incoming_replies_max_depth: 100,\n federation_reachability_timeout_days: 7,\n federation_publisher_modules: [Pleroma.Web.ActivityPub.Publisher],\n allow_relay: true,\n rewrite_policy: Pleroma.Web.ActivityPub.MRF.NoOpPolicy,\n public: true,\n quarantined_instances: [],\n managed_config: true,\n static_dir: \"instance/static/\",\n allowed_post_formats: [\"text/plain\", \"text/html\", \"text/markdown\", \"text/bbcode\"],\n mrf_transparency: true,\n mrf_transparency_exclusions: [],\n autofollowed_nicknames: [],\n max_pinned_statuses: 1,\n no_attachment_links: true,\n welcome_user_nickname: nil,\n welcome_message: nil,\n max_report_comment_size: 1000,\n safe_dm_mentions: false,\n healthcheck: false,\n remote_post_retention_days: 90,\n skip_thread_containment: true,\n limit_to_local_content: :unauthenticated,\n dynamic_configuration: false,\n user_bio_length: 5000,\n user_name_length: 100,\n max_account_fields: 10,\n max_remote_account_fields: 20,\n account_field_name_length: 512,\n account_field_value_length: 2048,\n external_user_synchronization: true,\n extended_nickname_format: true,\n multi_factor_authentication: [\n totp: [digits: 6, period: 30],\n backup_codes: [number: 2, length: 6]\n ]\n"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -914,6 +914,16 @@ test "unblocks domains" do
|
||||||
|
|
||||||
refute User.blocks?(user, collateral_user)
|
refute User.blocks?(user, collateral_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "follows take precedence over domain blocks" do
|
||||||
|
user = insert(:user)
|
||||||
|
good_eggo = insert(:user, %{ap_id: "https://meanies.social/user/cuteposter"})
|
||||||
|
|
||||||
|
{:ok, user} = User.block_domain(user, "meanies.social")
|
||||||
|
{:ok, user} = User.follow(user, good_eggo)
|
||||||
|
|
||||||
|
refute User.blocks?(user, good_eggo)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "blocks_import" do
|
describe "blocks_import" do
|
||||||
|
|
|
@ -608,6 +608,39 @@ test "doesn't return activities from blocked domains" do
|
||||||
refute repeat_activity in activities
|
refute repeat_activity in activities
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "does return activities from followed users on blocked domains" do
|
||||||
|
domain = "meanies.social"
|
||||||
|
domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
|
||||||
|
blocker = insert(:user)
|
||||||
|
|
||||||
|
{:ok, blocker} = User.follow(blocker, domain_user)
|
||||||
|
{:ok, blocker} = User.block_domain(blocker, domain)
|
||||||
|
|
||||||
|
assert User.following?(blocker, domain_user)
|
||||||
|
assert User.blocks_domain?(blocker, domain_user)
|
||||||
|
refute User.blocks?(blocker, domain_user)
|
||||||
|
|
||||||
|
note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
|
||||||
|
activity = insert(:note_activity, %{note: note})
|
||||||
|
|
||||||
|
activities =
|
||||||
|
ActivityPub.fetch_activities([], %{"blocking_user" => blocker, "skip_preload" => true})
|
||||||
|
|
||||||
|
assert activity in activities
|
||||||
|
|
||||||
|
# And check that if the guy we DO follow boosts someone else from their domain,
|
||||||
|
# that should be hidden
|
||||||
|
another_user = insert(:user, %{ap_id: "https://#{domain}/@meanie2"})
|
||||||
|
bad_note = insert(:note, %{data: %{"actor" => another_user.ap_id}})
|
||||||
|
bad_activity = insert(:note_activity, %{note: bad_note})
|
||||||
|
{:ok, repeat_activity, _} = CommonAPI.repeat(bad_activity.id, domain_user)
|
||||||
|
|
||||||
|
activities =
|
||||||
|
ActivityPub.fetch_activities([], %{"blocking_user" => blocker, "skip_preload" => true})
|
||||||
|
|
||||||
|
refute repeat_activity in activities
|
||||||
|
end
|
||||||
|
|
||||||
test "doesn't return muted activities" do
|
test "doesn't return muted activities" do
|
||||||
activity_one = insert(:note_activity)
|
activity_one = insert(:note_activity)
|
||||||
activity_two = insert(:note_activity)
|
activity_two = insert(:note_activity)
|
||||||
|
|
|
@ -25,6 +25,60 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
clear_config([:auth, :enforce_oauth_admin_scope_usage]) do
|
||||||
|
Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], false)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "with [:auth, :enforce_oauth_admin_scope_usage]," do
|
||||||
|
clear_config([:auth, :enforce_oauth_admin_scope_usage]) do
|
||||||
|
Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], true)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope" do
|
||||||
|
user = insert(:user)
|
||||||
|
admin = insert(:user, is_admin: true)
|
||||||
|
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(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(conn, :forbidden)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "DELETE /api/pleroma/admin/users" do
|
describe "DELETE /api/pleroma/admin/users" do
|
||||||
test "single user" do
|
test "single user" do
|
||||||
admin = insert(:user, is_admin: true)
|
admin = insert(:user, is_admin: true)
|
||||||
|
@ -98,7 +152,7 @@ test "Create" do
|
||||||
assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
|
assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
|
||||||
end
|
end
|
||||||
|
|
||||||
test "Cannot create user with exisiting email" do
|
test "Cannot create user with existing email" do
|
||||||
admin = insert(:user, is_admin: true)
|
admin = insert(:user, is_admin: true)
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
@ -129,7 +183,7 @@ test "Cannot create user with exisiting email" do
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "Cannot create user with exisiting nickname" do
|
test "Cannot create user with existing nickname" do
|
||||||
admin = insert(:user, is_admin: true)
|
admin = insert(:user, is_admin: true)
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
@ -1560,7 +1614,8 @@ test "returns 403 when requested by a non-admin" do
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> get("/api/pleroma/admin/reports")
|
|> get("/api/pleroma/admin/reports")
|
||||||
|
|
||||||
assert json_response(conn, :forbidden) == %{"error" => "User is not admin."}
|
assert json_response(conn, :forbidden) ==
|
||||||
|
%{"error" => "User is not an admin or OAuth admin scope is not granted."}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns 403 when requested by anonymous" do
|
test "returns 403 when requested by anonymous" do
|
||||||
|
|
|
@ -66,6 +66,7 @@ test "Represent a user account" do
|
||||||
note: "valid html",
|
note: "valid html",
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
|
actor_type: "Person",
|
||||||
discoverable: false
|
discoverable: false
|
||||||
},
|
},
|
||||||
fields: []
|
fields: []
|
||||||
|
@ -106,7 +107,8 @@ test "Represent a Service(bot) account" do
|
||||||
insert(:user, %{
|
insert(:user, %{
|
||||||
follower_count: 3,
|
follower_count: 3,
|
||||||
note_count: 5,
|
note_count: 5,
|
||||||
source_data: %{"type" => "Service"},
|
source_data: %{},
|
||||||
|
actor_type: "Service",
|
||||||
nickname: "shp@shitposter.club",
|
nickname: "shp@shitposter.club",
|
||||||
inserted_at: ~N[2017-08-15 15:47:06.597036]
|
inserted_at: ~N[2017-08-15 15:47:06.597036]
|
||||||
})
|
})
|
||||||
|
@ -134,6 +136,7 @@ test "Represent a Service(bot) account" do
|
||||||
note: user.bio,
|
note: user.bio,
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
|
actor_type: "Service",
|
||||||
discoverable: false
|
discoverable: false
|
||||||
},
|
},
|
||||||
fields: []
|
fields: []
|
||||||
|
@ -278,7 +281,8 @@ test "represent an embedded relationship" do
|
||||||
insert(:user, %{
|
insert(:user, %{
|
||||||
follower_count: 0,
|
follower_count: 0,
|
||||||
note_count: 5,
|
note_count: 5,
|
||||||
source_data: %{"type" => "Service"},
|
source_data: %{},
|
||||||
|
actor_type: "Service",
|
||||||
nickname: "shp@shitposter.club",
|
nickname: "shp@shitposter.club",
|
||||||
inserted_at: ~N[2017-08-15 15:47:06.597036]
|
inserted_at: ~N[2017-08-15 15:47:06.597036]
|
||||||
})
|
})
|
||||||
|
@ -311,6 +315,7 @@ test "represent an embedded relationship" do
|
||||||
note: user.bio,
|
note: user.bio,
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
|
actor_type: "Service",
|
||||||
discoverable: false
|
discoverable: false
|
||||||
},
|
},
|
||||||
fields: []
|
fields: []
|
||||||
|
|
Loading…
Reference in a new issue