Merge branch 'develop' into admin-be
This commit is contained in:
commit
503d966e9f
57 changed files with 651 additions and 254 deletions
|
|
@ -33,6 +33,7 @@ defmodule Pleroma.Application do
|
|||
def start(_type, _args) do
|
||||
Pleroma.HTML.compile_scrubbers()
|
||||
Pleroma.Config.DeprecationWarnings.warn()
|
||||
Pleroma.Repo.check_migrations_applied!()
|
||||
setup_instrumenters()
|
||||
load_custom_modules()
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ defmodule Pleroma.Plugs.UserIsAdminPlug do
|
|||
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.
|
||||
# Admin might opt out of admin scope for some apps to block any admin actions from them.
|
||||
conn
|
||||
|
||||
true ->
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ defmodule Pleroma.Repo do
|
|||
adapter: Ecto.Adapters.Postgres,
|
||||
migration_timestamps: [type: :naive_datetime_usec]
|
||||
|
||||
require Logger
|
||||
|
||||
defmodule Instrumenter do
|
||||
use Prometheus.EctoInstrumenter
|
||||
end
|
||||
|
|
@ -47,4 +49,37 @@ defmodule Pleroma.Repo do
|
|||
_ -> {:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
def check_migrations_applied!() do
|
||||
unless Pleroma.Config.get(
|
||||
[:i_am_aware_this_may_cause_data_loss, :disable_migration_check],
|
||||
false
|
||||
) do
|
||||
Ecto.Migrator.with_repo(__MODULE__, fn repo ->
|
||||
down_migrations =
|
||||
Ecto.Migrator.migrations(repo)
|
||||
|> Enum.reject(fn
|
||||
{:up, _, _} -> true
|
||||
{:down, _, _} -> false
|
||||
end)
|
||||
|
||||
if length(down_migrations) > 0 do
|
||||
down_migrations_text =
|
||||
Enum.map(down_migrations, fn {:down, id, name} -> "- #{name} (#{id})\n" end)
|
||||
|
||||
Logger.error(
|
||||
"The following migrations were not applied:\n#{down_migrations_text}If you want to start Pleroma anyway, set\nconfig :pleroma, :i_am_aware_this_may_cause_data_loss, disable_migration_check: true"
|
||||
)
|
||||
|
||||
raise Pleroma.Repo.UnappliedMigrationsError
|
||||
end
|
||||
end)
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Pleroma.Repo.UnappliedMigrationsError do
|
||||
defexception message: "Unapplied Migrations detected"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1874,22 +1874,13 @@ defmodule Pleroma.User do
|
|||
end
|
||||
|
||||
def admin_api_update(user, params) do
|
||||
changeset =
|
||||
cast(user, params, [
|
||||
:is_moderator,
|
||||
:is_admin,
|
||||
:show_role
|
||||
])
|
||||
|
||||
with {:ok, updated_user} <- update_and_set_cache(changeset) do
|
||||
if user.is_admin != updated_user.is_admin do
|
||||
# Admin status change results in change of accessible OAuth scopes, and instead of changing
|
||||
# already issued tokens we revoke them, requiring user to sign in again
|
||||
global_sign_out(user)
|
||||
end
|
||||
|
||||
{:ok, updated_user}
|
||||
end
|
||||
user
|
||||
|> cast(params, [
|
||||
:is_moderator,
|
||||
:is_admin,
|
||||
:show_role
|
||||
])
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
@doc "Signs user out of all applications"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do
|
|||
def filter(%{"type" => message_type} = message) do
|
||||
with accepted_vocabulary <- Pleroma.Config.get([:mrf_vocabulary, :accept]),
|
||||
rejected_vocabulary <- Pleroma.Config.get([:mrf_vocabulary, :reject]),
|
||||
true <- accepted_vocabulary == [] || Enum.member?(accepted_vocabulary, message_type),
|
||||
true <-
|
||||
Enum.empty?(accepted_vocabulary) || Enum.member?(accepted_vocabulary, message_type),
|
||||
false <-
|
||||
length(rejected_vocabulary) > 0 && Enum.member?(rejected_vocabulary, message_type),
|
||||
{:ok, _} <- filter(message["object"]) do
|
||||
|
|
|
|||
|
|
@ -658,24 +658,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do
|
||||
{:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
|
||||
|
||||
locked = new_user_data[:locked] || false
|
||||
attachment = get_in(new_user_data, [:source_data, "attachment"]) || []
|
||||
invisible = new_user_data[:invisible] || false
|
||||
|
||||
fields =
|
||||
attachment
|
||||
|> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
|
||||
|> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
|
||||
|
||||
update_data =
|
||||
new_user_data
|
||||
|> Map.take([:avatar, :banner, :bio, :name, :also_known_as])
|
||||
|> Map.put(:fields, fields)
|
||||
|> Map.put(:locked, locked)
|
||||
|> Map.put(:invisible, invisible)
|
||||
|
||||
actor
|
||||
|> User.upgrade_changeset(update_data, true)
|
||||
|> User.upgrade_changeset(new_user_data, true)
|
||||
|> User.update_and_set_cache()
|
||||
|
||||
ActivityPub.update(%{
|
||||
|
|
|
|||
|
|
@ -36,19 +36,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read:accounts"], admin: true}
|
||||
when action in [:list_users, :user_show, :right_get, :invites]
|
||||
when action in [:list_users, :user_show, :right_get]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:accounts"], admin: true}
|
||||
when action in [
|
||||
:get_invite_token,
|
||||
:revoke_invite,
|
||||
:email_invite,
|
||||
:get_password_reset,
|
||||
:user_follow,
|
||||
:user_unfollow,
|
||||
:user_delete,
|
||||
:users_create,
|
||||
:user_toggle_activation,
|
||||
|
|
@ -61,6 +56,20 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
]
|
||||
)
|
||||
|
||||
plug(OAuthScopesPlug, %{scopes: ["read:invites"], admin: true} when action == :invites)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:invites"], admin: true}
|
||||
when action in [:create_invite_token, :revoke_invite, :email_invite]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:follows"], admin: true}
|
||||
when action in [:user_follow, :user_unfollow, :relay_follow, :relay_unfollow]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read:reports"], admin: true}
|
||||
|
|
@ -70,7 +79,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:reports"], admin: true}
|
||||
when action in [:report_update_state, :report_respond]
|
||||
when action in [:reports_update]
|
||||
)
|
||||
|
||||
plug(
|
||||
|
|
@ -94,7 +103,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write"], admin: true}
|
||||
when action in [:relay_follow, :relay_unfollow, :config_update]
|
||||
when action == :config_update
|
||||
)
|
||||
|
||||
action_fallback(:errors)
|
||||
|
|
@ -632,7 +641,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
def force_password_reset(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||
users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
|
||||
|
||||
Enum.map(users, &User.force_password_reset_async/1)
|
||||
Enum.each(users, &User.force_password_reset_async/1)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
actor: admin,
|
||||
|
|
|
|||
|
|
@ -85,9 +85,13 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
def repeat(id_or_ap_id, user, params \\ %{}) do
|
||||
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||
object <- Object.normalize(activity),
|
||||
nil <- Utils.get_existing_announce(user.ap_id, object),
|
||||
announce_activity <- Utils.get_existing_announce(user.ap_id, object),
|
||||
public <- public_announce?(object, params) do
|
||||
ActivityPub.announce(user, object, nil, true, public)
|
||||
if announce_activity do
|
||||
{:ok, announce_activity, object}
|
||||
else
|
||||
ActivityPub.announce(user, object, nil, true, public)
|
||||
end
|
||||
else
|
||||
_ -> {:error, dgettext("errors", "Could not repeat")}
|
||||
end
|
||||
|
|
@ -105,8 +109,12 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
def favorite(id_or_ap_id, user) do
|
||||
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||
object <- Object.normalize(activity),
|
||||
nil <- Utils.get_existing_like(user.ap_id, object) do
|
||||
ActivityPub.like(user, object)
|
||||
like_activity <- Utils.get_existing_like(user.ap_id, object) do
|
||||
if like_activity do
|
||||
{:ok, like_activity, object}
|
||||
else
|
||||
ActivityPub.like(user, object)
|
||||
end
|
||||
else
|
||||
_ -> {:error, dgettext("errors", "Could not favorite")}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -23,6 +23,23 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do
|
|||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||
|
||||
# GET /api/v1/notifications
|
||||
def index(conn, %{"account_id" => account_id} = params) do
|
||||
case Pleroma.User.get_cached_by_id(account_id) do
|
||||
%{ap_id: account_ap_id} ->
|
||||
params =
|
||||
params
|
||||
|> Map.delete("account_id")
|
||||
|> Map.put("account_ap_id", account_ap_id)
|
||||
|
||||
index(conn, params)
|
||||
|
||||
_ ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{"error" => "Account is not found"})
|
||||
end
|
||||
end
|
||||
|
||||
def index(%{assigns: %{user: user}} = conn, params) do
|
||||
notifications = MastodonAPI.get_notifications(user, params)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do
|
|||
@moduledoc "The module represents functions to manage user subscriptions."
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Web.MastodonAPI.PushSubscriptionView, as: View
|
||||
alias Pleroma.Web.Push
|
||||
alias Pleroma.Web.Push.Subscription
|
||||
alias Pleroma.Web.MastodonAPI.PushSubscriptionView, as: View
|
||||
|
||||
action_fallback(:errors)
|
||||
|
||||
|
|
|
|||
|
|
@ -77,10 +77,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
|||
|> render("index.json", activities: activities, for: user, as: :activity)
|
||||
end
|
||||
|
||||
# GET /api/v1/timelines/tag/:tag
|
||||
def hashtag(%{assigns: %{user: user}} = conn, params) do
|
||||
local_only = truthy_param?(params["local"])
|
||||
|
||||
def hashtag_fetching(params, user, local_only) do
|
||||
tags =
|
||||
[params["tag"], params["any"]]
|
||||
|> List.flatten()
|
||||
|
|
@ -98,7 +95,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
|||
|> Map.get("none", [])
|
||||
|> Enum.map(&String.downcase(&1))
|
||||
|
||||
activities =
|
||||
_activities =
|
||||
params
|
||||
|> Map.put("type", "Create")
|
||||
|> Map.put("local_only", local_only)
|
||||
|
|
@ -109,6 +106,13 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
|||
|> Map.put("tag_all", tag_all)
|
||||
|> Map.put("tag_reject", tag_reject)
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
end
|
||||
|
||||
# GET /api/v1/timelines/tag/:tag
|
||||
def hashtag(%{assigns: %{user: user}} = conn, params) do
|
||||
local_only = truthy_param?(params["local"])
|
||||
|
||||
activities = hashtag_fetching(params, user, local_only)
|
||||
|
||||
conn
|
||||
|> add_link_headers(activities, %{"local" => local_only})
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
|
|||
user
|
||||
|> Notification.for_user_query(options)
|
||||
|> restrict(:exclude_types, options)
|
||||
|> restrict(:account_ap_id, options)
|
||||
|> Pagination.fetch_paginated(params)
|
||||
end
|
||||
|
||||
|
|
@ -71,7 +72,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
|
|||
exclude_visibilities: {:array, :string},
|
||||
reblogs: :boolean,
|
||||
with_muted: :boolean,
|
||||
with_move: :boolean
|
||||
with_move: :boolean,
|
||||
account_ap_id: :string
|
||||
}
|
||||
|
||||
changeset = cast({%{}, param_types}, params, Map.keys(param_types))
|
||||
|
|
@ -88,5 +90,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
|
|||
|> where([q, a], not fragment("? @> ARRAY[?->>'type']::varchar[]", ^ap_types, a.data))
|
||||
end
|
||||
|
||||
defp restrict(query, :account_ap_id, %{account_ap_id: account_ap_id}) do
|
||||
where(query, [n, a], a.actor == ^account_ap_id)
|
||||
end
|
||||
|
||||
defp restrict(query, _, _), do: query
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
alias Pleroma.Web.ControllerHelper
|
||||
alias Pleroma.Web.OAuth.App
|
||||
alias Pleroma.Web.OAuth.Authorization
|
||||
alias Pleroma.Web.OAuth.Scopes
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
alias Pleroma.Web.OAuth.Token.Strategy.RefreshToken
|
||||
alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken
|
||||
alias Pleroma.Web.OAuth.Scopes
|
||||
|
||||
require Logger
|
||||
|
||||
|
|
@ -222,7 +222,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
{:user_active, true} <- {:user_active, !user.deactivated},
|
||||
{:password_reset_pending, false} <-
|
||||
{:password_reset_pending, user.password_reset_pending},
|
||||
{:ok, scopes} <- validate_scopes(app, params, user),
|
||||
{:ok, scopes} <- validate_scopes(app, params),
|
||||
{:ok, auth} <- Authorization.create_authorization(app, user, scopes),
|
||||
{:ok, token} <- Token.exchange_token(app, auth) do
|
||||
json(conn, Token.Response.build(user, token))
|
||||
|
|
@ -471,7 +471,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
{:get_user, (user && {:ok, user}) || Authenticator.get_user(conn)},
|
||||
%App{} = app <- Repo.get_by(App, client_id: client_id),
|
||||
true <- redirect_uri in String.split(app.redirect_uris),
|
||||
{:ok, scopes} <- validate_scopes(app, auth_attrs, user),
|
||||
{:ok, scopes} <- validate_scopes(app, auth_attrs),
|
||||
{:auth_active, true} <- {:auth_active, User.auth_active?(user)} do
|
||||
Authorization.create_authorization(app, user, scopes)
|
||||
end
|
||||
|
|
@ -487,12 +487,12 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
defp put_session_registration_id(%Plug.Conn{} = conn, registration_id),
|
||||
do: put_session(conn, :registration_id, registration_id)
|
||||
|
||||
@spec validate_scopes(App.t(), map(), User.t()) ::
|
||||
@spec validate_scopes(App.t(), map()) ::
|
||||
{:ok, list()} | {:error, :missing_scopes | :unsupported_scopes}
|
||||
defp validate_scopes(%App{} = app, params, %User{} = user) do
|
||||
defp validate_scopes(%App{} = app, params) do
|
||||
params
|
||||
|> Scopes.fetch_scopes(app.scopes)
|
||||
|> Scopes.validate(app.scopes, user)
|
||||
|> Scopes.validate(app.scopes)
|
||||
end
|
||||
|
||||
def default_redirect_uri(%App{} = app) do
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ defmodule Pleroma.Web.OAuth.Scopes do
|
|||
"""
|
||||
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.User
|
||||
|
||||
@doc """
|
||||
Fetch scopes from request params.
|
||||
|
|
@ -56,35 +55,18 @@ defmodule Pleroma.Web.OAuth.Scopes do
|
|||
@doc """
|
||||
Validates scopes.
|
||||
"""
|
||||
@spec validate(list() | nil, list(), User.t()) ::
|
||||
@spec validate(list() | nil, list()) ::
|
||||
{:ok, list()} | {:error, :missing_scopes | :unsupported_scopes}
|
||||
def validate(blank_scopes, _app_scopes, _user) when blank_scopes in [nil, []],
|
||||
def validate(blank_scopes, _app_scopes) when blank_scopes in [nil, []],
|
||||
do: {:error, :missing_scopes}
|
||||
|
||||
def validate(scopes, app_scopes, %User{} = user) 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
|
||||
def validate(scopes, app_scopes) do
|
||||
case OAuthScopesPlug.filter_descendants(scopes, app_scopes) do
|
||||
^scopes -> {:ok, scopes}
|
||||
_ -> {:error, :unsupported_scopes}
|
||||
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
|
||||
# Gracefully dropping admin scopes from requested scopes if user isn't an admin (not raising)
|
||||
scopes = scopes -- OAuthScopesPlug.filter_descendants(scopes, ["admin"])
|
||||
validate(scopes, app_scopes, user)
|
||||
end
|
||||
end
|
||||
|
||||
def contains_admin_scopes?(scopes) do
|
||||
scopes
|
||||
|> OAuthScopesPlug.filter_descendants(["admin"])
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
|||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read:statuses"]}
|
||||
when action in [:conversation, :conversation_statuses, :emoji_reactions_by]
|
||||
when action in [:conversation, :conversation_statuses]
|
||||
)
|
||||
|
||||
plug(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue