Merge branch 'develop' into feature/custom-runtime-modules
This commit is contained in:
commit
7528322f83
69 changed files with 1129 additions and 1264 deletions
|
|
@ -8,7 +8,6 @@ defmodule Mix.Tasks.Pleroma.User do
|
|||
alias Ecto.Changeset
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserInviteToken
|
||||
alias Pleroma.Web.OAuth
|
||||
|
||||
@shortdoc "Manages Pleroma users"
|
||||
@moduledoc File.read!("docs/administration/CLI_tasks/user.md")
|
||||
|
|
@ -354,8 +353,7 @@ defmodule Mix.Tasks.Pleroma.User do
|
|||
start_pleroma()
|
||||
|
||||
with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
|
||||
OAuth.Token.delete_user_tokens(user)
|
||||
OAuth.Authorization.delete_user_authorizations(user)
|
||||
User.global_sign_out(user)
|
||||
|
||||
shell_info("#{nickname} signed out from all apps.")
|
||||
else
|
||||
|
|
@ -393,10 +391,7 @@ defmodule Mix.Tasks.Pleroma.User do
|
|||
end
|
||||
|
||||
defp set_admin(user, value) do
|
||||
{:ok, user} =
|
||||
user
|
||||
|> Changeset.change(%{is_admin: value})
|
||||
|> User.update_and_set_cache()
|
||||
{:ok, user} = User.admin_api_update(user, %{is_admin: value})
|
||||
|
||||
shell_info("Admin status of #{user.nickname}: #{user.is_admin}")
|
||||
user
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ defmodule Pleroma.Activity do
|
|||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.ReportNote
|
||||
alias Pleroma.ThreadMute
|
||||
alias Pleroma.User
|
||||
|
||||
|
|
@ -48,6 +49,8 @@ defmodule Pleroma.Activity do
|
|||
has_one(:user_actor, User, on_delete: :nothing, foreign_key: :id)
|
||||
# This is a fake relation, do not use outside of with_preloaded_bookmark/get_bookmark
|
||||
has_one(:bookmark, Bookmark)
|
||||
# This is a fake relation, do not use outside of with_preloaded_report_notes
|
||||
has_many(:report_notes, ReportNote)
|
||||
has_many(:notifications, Notification, on_delete: :delete_all)
|
||||
|
||||
# Attention: this is a fake relation, don't try to preload it blindly and expect it to work!
|
||||
|
|
@ -114,6 +117,16 @@ defmodule Pleroma.Activity do
|
|||
|
||||
def with_preloaded_bookmark(query, _), do: query
|
||||
|
||||
def with_preloaded_report_notes(query) do
|
||||
from([a] in query,
|
||||
left_join: r in ReportNote,
|
||||
on: a.id == r.activity_id,
|
||||
preload: [report_notes: r]
|
||||
)
|
||||
end
|
||||
|
||||
def with_preloaded_report_notes(query, _), do: query
|
||||
|
||||
def with_set_thread_muted_field(query, %User{} = user) do
|
||||
from([a] in query,
|
||||
left_join: tm in ThreadMute,
|
||||
|
|
|
|||
35
lib/pleroma/captcha/native.ex
Normal file
35
lib/pleroma/captcha/native.ex
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Captcha.Native do
|
||||
import Pleroma.Web.Gettext
|
||||
alias Pleroma.Captcha.Service
|
||||
@behaviour Service
|
||||
|
||||
@impl Service
|
||||
def new do
|
||||
case Captcha.get() do
|
||||
{:timeout} ->
|
||||
%{error: dgettext("errors", "Captcha timeout")}
|
||||
|
||||
{:ok, answer_data, img_binary} ->
|
||||
%{
|
||||
type: :native,
|
||||
token: token(),
|
||||
url: "data:image/png;base64," <> Base.encode64(img_binary),
|
||||
answer_data: answer_data
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@impl Service
|
||||
def validate(_token, captcha, captcha) when not is_nil(captcha), do: :ok
|
||||
def validate(_token, _captcha, _answer), do: {:error, dgettext("errors", "Invalid CAPTCHA")}
|
||||
|
||||
defp token do
|
||||
10
|
||||
|> :crypto.strong_rand_bytes()
|
||||
|> Base.url_encode64(padding: false)
|
||||
end
|
||||
end
|
||||
|
|
@ -65,4 +65,16 @@ defmodule Pleroma.Config do
|
|||
def oauth_consumer_strategies, do: get([:auth, :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
|
||||
|
|
|
|||
|
|
@ -128,17 +128,35 @@ defmodule Pleroma.ModerationLog do
|
|||
{:ok, ModerationLog} | {:error, any}
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
action: "report_response",
|
||||
action: "report_note",
|
||||
subject: %Activity{} = subject,
|
||||
text: text
|
||||
}) do
|
||||
%ModerationLog{
|
||||
data: %{
|
||||
"actor" => user_to_map(actor),
|
||||
"action" => "report_response",
|
||||
"action" => "report_note",
|
||||
"subject" => report_to_map(subject),
|
||||
"text" => text,
|
||||
"message" => ""
|
||||
"text" => text
|
||||
}
|
||||
}
|
||||
|> insert_log_entry_with_message()
|
||||
end
|
||||
|
||||
@spec insert_log(%{actor: User, subject: Activity, action: String.t(), text: String.t()}) ::
|
||||
{:ok, ModerationLog} | {:error, any}
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
action: "report_note_delete",
|
||||
subject: %Activity{} = subject,
|
||||
text: text
|
||||
}) do
|
||||
%ModerationLog{
|
||||
data: %{
|
||||
"actor" => user_to_map(actor),
|
||||
"action" => "report_note_delete",
|
||||
"subject" => report_to_map(subject),
|
||||
"text" => text
|
||||
}
|
||||
}
|
||||
|> insert_log_entry_with_message()
|
||||
|
|
@ -556,12 +574,24 @@ defmodule Pleroma.ModerationLog do
|
|||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "report_response",
|
||||
"action" => "report_note",
|
||||
"subject" => %{"id" => subject_id, "type" => "report"},
|
||||
"text" => text
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} responded with '#{text}' to report ##{subject_id}"
|
||||
"@#{actor_nickname} added note '#{text}' to report ##{subject_id}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "report_note_delete",
|
||||
"subject" => %{"id" => subject_id, "type" => "report"},
|
||||
"text" => text
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} deleted note '#{text}' from report ##{subject_id}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
|
|
|
|||
|
|
@ -23,6 +23,23 @@ defmodule Pleroma.Object do
|
|||
timestamps()
|
||||
end
|
||||
|
||||
def with_joined_activity(query, activity_type \\ "Create", join_type \\ :inner) do
|
||||
object_position = Map.get(query.aliases, :object, 0)
|
||||
|
||||
join(query, join_type, [{object, object_position}], a in Activity,
|
||||
on:
|
||||
fragment(
|
||||
"COALESCE(?->'object'->>'id', ?->>'object') = (? ->> 'id') AND (?->>'type' = ?) ",
|
||||
a.data,
|
||||
a.data,
|
||||
object.data,
|
||||
a.data,
|
||||
^activity_type
|
||||
),
|
||||
as: :object_activity
|
||||
)
|
||||
end
|
||||
|
||||
def create(data) do
|
||||
Object.change(%Object{}, %{data: data})
|
||||
|> Repo.insert()
|
||||
|
|
|
|||
|
|
@ -13,60 +13,66 @@ defmodule Pleroma.Pagination do
|
|||
alias Pleroma.Repo
|
||||
|
||||
@default_limit 20
|
||||
@page_keys ["max_id", "min_id", "limit", "since_id", "order"]
|
||||
|
||||
def fetch_paginated(query, params, type \\ :keyset)
|
||||
def page_keys, do: @page_keys
|
||||
|
||||
def fetch_paginated(query, %{"total" => true} = params, :keyset) do
|
||||
def fetch_paginated(query, params, type \\ :keyset, table_binding \\ nil)
|
||||
|
||||
def fetch_paginated(query, %{"total" => true} = params, :keyset, table_binding) do
|
||||
total = Repo.aggregate(query, :count, :id)
|
||||
|
||||
%{
|
||||
total: total,
|
||||
items: fetch_paginated(query, Map.drop(params, ["total"]), :keyset)
|
||||
items: fetch_paginated(query, Map.drop(params, ["total"]), :keyset, table_binding)
|
||||
}
|
||||
end
|
||||
|
||||
def fetch_paginated(query, params, :keyset) do
|
||||
def fetch_paginated(query, params, :keyset, table_binding) do
|
||||
options = cast_params(params)
|
||||
|
||||
query
|
||||
|> paginate(options, :keyset)
|
||||
|> paginate(options, :keyset, table_binding)
|
||||
|> Repo.all()
|
||||
|> enforce_order(options)
|
||||
end
|
||||
|
||||
def fetch_paginated(query, %{"total" => true} = params, :offset) do
|
||||
total = Repo.aggregate(query, :count, :id)
|
||||
def fetch_paginated(query, %{"total" => true} = params, :offset, table_binding) do
|
||||
total =
|
||||
query
|
||||
|> Ecto.Query.exclude(:left_join)
|
||||
|> Repo.aggregate(:count, :id)
|
||||
|
||||
%{
|
||||
total: total,
|
||||
items: fetch_paginated(query, Map.drop(params, ["total"]), :offset)
|
||||
items: fetch_paginated(query, Map.drop(params, ["total"]), :offset, table_binding)
|
||||
}
|
||||
end
|
||||
|
||||
def fetch_paginated(query, params, :offset) do
|
||||
def fetch_paginated(query, params, :offset, table_binding) do
|
||||
options = cast_params(params)
|
||||
|
||||
query
|
||||
|> paginate(options, :offset)
|
||||
|> paginate(options, :offset, table_binding)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
def paginate(query, options, method \\ :keyset)
|
||||
def paginate(query, options, method \\ :keyset, table_binding \\ nil)
|
||||
|
||||
def paginate(query, options, :keyset) do
|
||||
def paginate(query, options, :keyset, table_binding) do
|
||||
query
|
||||
|> restrict(:min_id, options)
|
||||
|> restrict(:since_id, options)
|
||||
|> restrict(:max_id, options)
|
||||
|> restrict(:order, options)
|
||||
|> restrict(:limit, options)
|
||||
|> restrict(:min_id, options, table_binding)
|
||||
|> restrict(:since_id, options, table_binding)
|
||||
|> restrict(:max_id, options, table_binding)
|
||||
|> restrict(:order, options, table_binding)
|
||||
|> restrict(:limit, options, table_binding)
|
||||
end
|
||||
|
||||
def paginate(query, options, :offset) do
|
||||
def paginate(query, options, :offset, table_binding) do
|
||||
query
|
||||
|> restrict(:order, options)
|
||||
|> restrict(:offset, options)
|
||||
|> restrict(:limit, options)
|
||||
|> restrict(:order, options, table_binding)
|
||||
|> restrict(:offset, options, table_binding)
|
||||
|> restrict(:limit, options, table_binding)
|
||||
end
|
||||
|
||||
defp cast_params(params) do
|
||||
|
|
@ -75,7 +81,8 @@ defmodule Pleroma.Pagination do
|
|||
since_id: :string,
|
||||
max_id: :string,
|
||||
offset: :integer,
|
||||
limit: :integer
|
||||
limit: :integer,
|
||||
skip_order: :boolean
|
||||
}
|
||||
|
||||
params =
|
||||
|
|
@ -88,38 +95,48 @@ defmodule Pleroma.Pagination do
|
|||
changeset.changes
|
||||
end
|
||||
|
||||
defp restrict(query, :min_id, %{min_id: min_id}) do
|
||||
where(query, [q], q.id > ^min_id)
|
||||
defp restrict(query, :min_id, %{min_id: min_id}, table_binding) do
|
||||
where(query, [{q, table_position(query, table_binding)}], q.id > ^min_id)
|
||||
end
|
||||
|
||||
defp restrict(query, :since_id, %{since_id: since_id}) do
|
||||
where(query, [q], q.id > ^since_id)
|
||||
defp restrict(query, :since_id, %{since_id: since_id}, table_binding) do
|
||||
where(query, [{q, table_position(query, table_binding)}], q.id > ^since_id)
|
||||
end
|
||||
|
||||
defp restrict(query, :max_id, %{max_id: max_id}) do
|
||||
where(query, [q], q.id < ^max_id)
|
||||
defp restrict(query, :max_id, %{max_id: max_id}, table_binding) do
|
||||
where(query, [{q, table_position(query, table_binding)}], q.id < ^max_id)
|
||||
end
|
||||
|
||||
defp restrict(query, :order, %{min_id: _}) do
|
||||
order_by(query, [u], fragment("? asc nulls last", u.id))
|
||||
defp restrict(query, :order, %{skip_order: true}, _), do: query
|
||||
|
||||
defp restrict(query, :order, %{min_id: _}, table_binding) do
|
||||
order_by(
|
||||
query,
|
||||
[{u, table_position(query, table_binding)}],
|
||||
fragment("? asc nulls last", u.id)
|
||||
)
|
||||
end
|
||||
|
||||
defp restrict(query, :order, _options) do
|
||||
order_by(query, [u], fragment("? desc nulls last", u.id))
|
||||
defp restrict(query, :order, _options, table_binding) do
|
||||
order_by(
|
||||
query,
|
||||
[{u, table_position(query, table_binding)}],
|
||||
fragment("? desc nulls last", u.id)
|
||||
)
|
||||
end
|
||||
|
||||
defp restrict(query, :offset, %{offset: offset}) do
|
||||
defp restrict(query, :offset, %{offset: offset}, _table_binding) do
|
||||
offset(query, ^offset)
|
||||
end
|
||||
|
||||
defp restrict(query, :limit, options) do
|
||||
defp restrict(query, :limit, options, _table_binding) do
|
||||
limit = Map.get(options, :limit, @default_limit)
|
||||
|
||||
query
|
||||
|> limit(^limit)
|
||||
end
|
||||
|
||||
defp restrict(query, _, _), do: query
|
||||
defp restrict(query, _, _, _), do: query
|
||||
|
||||
defp enforce_order(result, %{min_id: _}) do
|
||||
result
|
||||
|
|
@ -127,4 +144,10 @@ defmodule Pleroma.Pagination do
|
|||
end
|
||||
|
||||
defp enforce_order(result, _), do: result
|
||||
|
||||
defp table_position(%Ecto.Query{} = query, binding_name) do
|
||||
Map.get(query.aliases, binding_name, 0)
|
||||
end
|
||||
|
||||
defp table_position(_, _), do: 0
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do
|
|||
import Plug.Conn
|
||||
import Pleroma.Web.Gettext
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
|
||||
@behaviour Plug
|
||||
|
|
@ -15,6 +16,8 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do
|
|||
def call(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do
|
||||
op = options[:op] || :|
|
||||
token = assigns[:token]
|
||||
|
||||
scopes = transform_scopes(scopes, options)
|
||||
matched_scopes = token && filter_descendants(scopes, token.scopes)
|
||||
|
||||
cond do
|
||||
|
|
@ -60,6 +63,15 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do
|
|||
)
|
||||
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
|
||||
if options[:skip_instance_privacy_check] do
|
||||
conn
|
||||
|
|
|
|||
|
|
@ -5,19 +5,38 @@
|
|||
defmodule Pleroma.Plugs.UserIsAdminPlug do
|
||||
import Pleroma.Web.TranslationHelpers
|
||||
import Plug.Conn
|
||||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.OAuth
|
||||
|
||||
def init(options) do
|
||||
options
|
||||
end
|
||||
|
||||
def call(%{assigns: %{user: %User{is_admin: true}}} = conn, _) do
|
||||
conn
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
def call(conn, _) do
|
||||
fail(conn)
|
||||
end
|
||||
|
||||
defp fail(conn) do
|
||||
conn
|
||||
|> render_error(:forbidden, "User is not admin.")
|
||||
|> halt
|
||||
|> render_error(:forbidden, "User is not an admin or OAuth admin scope is not granted.")
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
|
|
|
|||
48
lib/pleroma/report_note.ex
Normal file
48
lib/pleroma/report_note.ex
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.ReportNote do
|
||||
use Ecto.Schema
|
||||
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.ReportNote
|
||||
alias Pleroma.User
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
schema "report_notes" do
|
||||
field(:content, :string)
|
||||
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
|
||||
belongs_to(:activity, Activity, type: FlakeId.Ecto.CompatType)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
@spec create(FlakeId.Ecto.CompatType.t(), FlakeId.Ecto.CompatType.t(), String.t()) ::
|
||||
{:ok, ReportNote.t()} | {:error, Changeset.t()}
|
||||
def create(user_id, activity_id, content) do
|
||||
attrs = %{
|
||||
user_id: user_id,
|
||||
activity_id: activity_id,
|
||||
content: content
|
||||
}
|
||||
|
||||
%ReportNote{}
|
||||
|> cast(attrs, [:user_id, :activity_id, :content])
|
||||
|> validate_required([:user_id, :activity_id, :content])
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
@spec destroy(FlakeId.Ecto.CompatType.t()) ::
|
||||
{:ok, ReportNote.t()} | {:error, Changeset.t()}
|
||||
def destroy(id) do
|
||||
from(r in ReportNote, where: r.id == ^id)
|
||||
|> Repo.one()
|
||||
|> Repo.delete()
|
||||
end
|
||||
end
|
||||
|
|
@ -862,6 +862,13 @@ defmodule Pleroma.User do
|
|||
|> Repo.all()
|
||||
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
|
||||
user
|
||||
|> get_friends_query(page)
|
||||
|
|
@ -1136,7 +1143,8 @@ defmodule Pleroma.User do
|
|||
def blocks?(nil, _), do: false
|
||||
|
||||
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
|
||||
|
||||
def blocks_user?(%User{} = user, %User{} = target) do
|
||||
|
|
@ -1839,13 +1847,28 @@ defmodule Pleroma.User do
|
|||
end
|
||||
|
||||
def admin_api_update(user, params) do
|
||||
user
|
||||
|> cast(params, [
|
||||
:is_moderator,
|
||||
:is_admin,
|
||||
:show_role
|
||||
])
|
||||
|> update_and_set_cache()
|
||||
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
|
||||
# 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
|
||||
|
||||
def mascot_update(user, url) do
|
||||
|
|
|
|||
|
|
@ -950,6 +950,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
blocked_ap_ids = opts["blocked_users_ap_ids"] || User.blocked_users_ap_ids(user)
|
||||
domain_blocks = user.domain_blocks || []
|
||||
|
||||
following_ap_ids = User.get_friends_ap_ids(user)
|
||||
|
||||
query =
|
||||
if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
|
||||
|
||||
|
|
@ -964,8 +966,22 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
activity.data,
|
||||
^blocked_ap_ids
|
||||
),
|
||||
where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks),
|
||||
where: fragment("not (split_part(?->>'actor', '/', 3) = ANY(?))", o.data, ^domain_blocks)
|
||||
where:
|
||||
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
|
||||
|
||||
|
|
@ -1052,6 +1068,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|> Activity.with_preloaded_bookmark(opts["user"])
|
||||
end
|
||||
|
||||
defp maybe_preload_report_notes(query, %{"preload_report_notes" => true}) do
|
||||
query
|
||||
|> Activity.with_preloaded_report_notes()
|
||||
end
|
||||
|
||||
defp maybe_preload_report_notes(query, _), do: query
|
||||
|
||||
defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
|
||||
|
||||
defp maybe_set_thread_muted_field(query, opts) do
|
||||
|
|
@ -1105,6 +1128,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
Activity
|
||||
|> maybe_preload_objects(opts)
|
||||
|> maybe_preload_bookmarks(opts)
|
||||
|> maybe_preload_report_notes(opts)
|
||||
|> maybe_set_thread_muted_field(opts)
|
||||
|> maybe_order(opts)
|
||||
|> restrict_recipients(recipients, opts["user"])
|
||||
|
|
@ -1141,6 +1165,25 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|> maybe_update_cc(list_memberships, opts["user"])
|
||||
end
|
||||
|
||||
@doc """
|
||||
Fetch favorites activities of user with order by sort adds to favorites
|
||||
"""
|
||||
@spec fetch_favourites(User.t(), map(), atom()) :: list(Activity.t())
|
||||
def fetch_favourites(user, params \\ %{}, pagination \\ :keyset) do
|
||||
user.ap_id
|
||||
|> Activity.Queries.by_actor()
|
||||
|> Activity.Queries.by_type("Like")
|
||||
|> Activity.with_joined_object()
|
||||
|> Object.with_joined_activity()
|
||||
|> select([_like, object, activity], %{activity | object: object})
|
||||
|> order_by([like, _, _], desc: like.id)
|
||||
|> Pagination.fetch_paginated(
|
||||
Map.merge(params, %{"skip_order" => true}),
|
||||
pagination,
|
||||
:object_activity
|
||||
)
|
||||
end
|
||||
|
||||
defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
|
||||
when is_list(list_memberships) and length(list_memberships) > 0 do
|
||||
Enum.map(activities, fn
|
||||
|
|
|
|||
|
|
@ -787,6 +787,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
params
|
||||
|> Map.put("type", "Flag")
|
||||
|> Map.put("skip_preload", true)
|
||||
|> Map.put("preload_report_notes", true)
|
||||
|> Map.put("total", true)
|
||||
|> Map.put("limit", page_size)
|
||||
|> Map.put("offset", (page - 1) * page_size)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
alias Pleroma.Activity
|
||||
alias Pleroma.ModerationLog
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.ReportNote
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserInviteToken
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
|
|
@ -30,13 +31,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read:accounts"]}
|
||||
%{scopes: ["read:accounts"], admin: true}
|
||||
when action in [:list_users, :user_show, :right_get, :invites]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:accounts"]}
|
||||
%{scopes: ["write:accounts"], admin: true}
|
||||
when action in [
|
||||
:get_invite_token,
|
||||
:revoke_invite,
|
||||
|
|
@ -58,35 +59,37 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read:reports"]} when action in [:list_reports, :report_show]
|
||||
%{scopes: ["read:reports"], admin: true}
|
||||
when action in [:list_reports, :report_show]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:reports"]}
|
||||
%{scopes: ["write:reports"], admin: true}
|
||||
when action in [:report_update_state, :report_respond]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read:statuses"]} when action == :list_user_statuses
|
||||
%{scopes: ["read:statuses"], admin: true}
|
||||
when action == :list_user_statuses
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:statuses"]}
|
||||
%{scopes: ["write:statuses"], admin: true}
|
||||
when action in [:status_update, :status_delete]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read"]}
|
||||
%{scopes: ["read"], admin: true}
|
||||
when action in [:config_show, :migrate_to_db, :migrate_from_db, :list_log]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write"]}
|
||||
%{scopes: ["write"], admin: true}
|
||||
when action in [:relay_follow, :relay_unfollow, :config_update]
|
||||
)
|
||||
|
||||
|
|
@ -238,7 +241,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
})
|
||||
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> put_view(Pleroma.Web.AdminAPI.StatusView)
|
||||
|> render("index.json", %{activities: activities, as: :activity})
|
||||
end
|
||||
|
||||
|
|
@ -641,9 +644,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
def list_reports(conn, params) do
|
||||
{page, page_size} = page_params(params)
|
||||
|
||||
reports = Utils.get_reports(params, page, page_size)
|
||||
|
||||
conn
|
||||
|> put_view(ReportView)
|
||||
|> render("index.json", %{reports: Utils.get_reports(params, page, page_size)})
|
||||
|> render("index.json", %{reports: reports})
|
||||
end
|
||||
|
||||
def list_grouped_reports(conn, _params) do
|
||||
|
|
@ -687,32 +692,39 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def report_respond(%{assigns: %{user: user}} = conn, %{"id" => id} = params) do
|
||||
with false <- is_nil(params["status"]),
|
||||
%Activity{} <- Activity.get_by_id(id) do
|
||||
params =
|
||||
params
|
||||
|> Map.put("in_reply_to_status_id", id)
|
||||
|> Map.put("visibility", "direct")
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, params)
|
||||
|
||||
def report_notes_create(%{assigns: %{user: user}} = conn, %{
|
||||
"id" => report_id,
|
||||
"content" => content
|
||||
}) do
|
||||
with {:ok, _} <- ReportNote.create(user.id, report_id, content) do
|
||||
ModerationLog.insert_log(%{
|
||||
action: "report_response",
|
||||
action: "report_note",
|
||||
actor: user,
|
||||
subject: activity,
|
||||
text: params["status"]
|
||||
subject: Activity.get_by_id(report_id),
|
||||
text: content
|
||||
})
|
||||
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> render("show.json", %{activity: activity})
|
||||
json_response(conn, :no_content, "")
|
||||
else
|
||||
true ->
|
||||
{:param_cast, nil}
|
||||
_ -> json_response(conn, :bad_request, "")
|
||||
end
|
||||
end
|
||||
|
||||
nil ->
|
||||
{:error, :not_found}
|
||||
def report_notes_delete(%{assigns: %{user: user}} = conn, %{
|
||||
"id" => note_id,
|
||||
"report_id" => report_id
|
||||
}) do
|
||||
with {:ok, note} <- ReportNote.destroy(note_id) do
|
||||
ModerationLog.insert_log(%{
|
||||
action: "report_note_delete",
|
||||
actor: user,
|
||||
subject: Activity.get_by_id(report_id),
|
||||
text: note.content
|
||||
})
|
||||
|
||||
json_response(conn, :no_content, "")
|
||||
else
|
||||
_ -> json_response(conn, :bad_request, "")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@ defmodule Pleroma.Web.AdminAPI.ReportView do
|
|||
content: content,
|
||||
created_at: created_at,
|
||||
statuses: StatusView.render("index.json", %{activities: statuses, as: :activity}),
|
||||
state: report.data["state"]
|
||||
state: report.data["state"],
|
||||
notes: render(__MODULE__, "index_notes.json", %{notes: report.report_notes})
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -69,6 +70,28 @@ defmodule Pleroma.Web.AdminAPI.ReportView do
|
|||
}
|
||||
end
|
||||
|
||||
def render("index_notes.json", %{notes: notes}) when is_list(notes) do
|
||||
Enum.map(notes, &render(__MODULE__, "show_note.json", &1))
|
||||
end
|
||||
|
||||
def render("index_notes.json", _), do: []
|
||||
|
||||
def render("show_note.json", %{
|
||||
id: id,
|
||||
content: content,
|
||||
user_id: user_id,
|
||||
inserted_at: inserted_at
|
||||
}) do
|
||||
user = User.get_by_id(user_id)
|
||||
|
||||
%{
|
||||
id: id,
|
||||
content: content,
|
||||
user: merge_account_views(user),
|
||||
created_at: Utils.to_masto_date(inserted_at)
|
||||
}
|
||||
end
|
||||
|
||||
defp merge_account_views(%User{} = user) do
|
||||
Pleroma.Web.MastodonAPI.AccountView.render("show.json", %{user: user})
|
||||
|> Map.merge(Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user}))
|
||||
|
|
|
|||
42
lib/pleroma/web/admin_api/views/status_view.ex
Normal file
42
lib/pleroma/web/admin_api/views/status_view.ex
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.AdminAPI.StatusView do
|
||||
use Pleroma.Web, :view
|
||||
|
||||
require Pleroma.Constants
|
||||
|
||||
alias Pleroma.User
|
||||
|
||||
def render("index.json", opts) do
|
||||
render_many(opts.activities, __MODULE__, "show.json", opts)
|
||||
end
|
||||
|
||||
def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
|
||||
user = get_user(activity.data["actor"])
|
||||
|
||||
Pleroma.Web.MastodonAPI.StatusView.render("show.json", opts)
|
||||
|> Map.merge(%{account: merge_account_views(user)})
|
||||
end
|
||||
|
||||
defp merge_account_views(%User{} = user) do
|
||||
Pleroma.Web.MastodonAPI.AccountView.render("show.json", %{user: user})
|
||||
|> Map.merge(Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user}))
|
||||
end
|
||||
|
||||
defp merge_account_views(_), do: %{}
|
||||
|
||||
defp get_user(ap_id) do
|
||||
cond do
|
||||
user = User.get_cached_by_ap_id(ap_id) ->
|
||||
user
|
||||
|
||||
user = User.get_by_guessed_nickname(ap_id) ->
|
||||
user
|
||||
|
||||
true ->
|
||||
User.error_user(ap_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -346,15 +346,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||
|
||||
@doc "GET /api/v1/favourites"
|
||||
def favourites(%{assigns: %{user: user}} = conn, params) do
|
||||
params =
|
||||
params
|
||||
|> Map.put("type", "Create")
|
||||
|> Map.put("favorited_by", user.ap_id)
|
||||
|> Map.put("blocking_user", user)
|
||||
|
||||
activities =
|
||||
ActivityPub.fetch_activities([], params)
|
||||
|> Enum.reverse()
|
||||
ActivityPub.fetch_favourites(
|
||||
user,
|
||||
Map.take(params, Pleroma.Pagination.page_keys())
|
||||
)
|
||||
|
||||
conn
|
||||
|> add_link_headers(activities)
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
{:ok, scopes} <- validate_scopes(app, params, user),
|
||||
{: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),
|
||||
{:ok, scopes} <- validate_scopes(app, auth_attrs, user),
|
||||
{: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()) ::
|
||||
@spec validate_scopes(App.t(), map(), User.t()) ::
|
||||
{:ok, list()} | {:error, :missing_scopes | :unsupported_scopes}
|
||||
defp validate_scopes(app, params) do
|
||||
defp validate_scopes(%App{} = app, params, %User{} = user) do
|
||||
params
|
||||
|> Scopes.fetch_scopes(app.scopes)
|
||||
|> Scopes.validate(app.scopes)
|
||||
|> Scopes.validate(app.scopes, user)
|
||||
end
|
||||
|
||||
def default_redirect_uri(%App{} = app) do
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ defmodule Pleroma.Web.OAuth.Scopes do
|
|||
Functions for dealing with scopes.
|
||||
"""
|
||||
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.User
|
||||
|
||||
@doc """
|
||||
Fetch scopes from request params.
|
||||
|
||||
|
|
@ -53,15 +56,38 @@ defmodule Pleroma.Web.OAuth.Scopes do
|
|||
@doc """
|
||||
Validates scopes.
|
||||
"""
|
||||
@spec validate(list() | nil, list()) ::
|
||||
@spec validate(list() | nil, list(), User.t()) ::
|
||||
{:ok, list()} | {:error, :missing_scopes | :unsupported_scopes}
|
||||
def validate([], _app_scopes), do: {:error, :missing_scopes}
|
||||
def validate(nil, _app_scopes), do: {:error, :missing_scopes}
|
||||
def validate(blank_scopes, _app_scopes, _user) when blank_scopes in [nil, []],
|
||||
do: {:error, :missing_scopes}
|
||||
|
||||
def validate(scopes, app_scopes) do
|
||||
case Pleroma.Plugs.OAuthScopesPlug.filter_descendants(scopes, app_scopes) do
|
||||
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
|
||||
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"])
|
||||
|> Enum.any?()
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
|
|||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write"]}
|
||||
%{scopes: ["write"], admin: true}
|
||||
when action in [
|
||||
:create,
|
||||
:delete,
|
||||
|
|
|
|||
|
|
@ -187,7 +187,8 @@ defmodule Pleroma.Web.Router do
|
|||
get("/grouped_reports", AdminAPIController, :list_grouped_reports)
|
||||
get("/reports/:id", AdminAPIController, :report_show)
|
||||
patch("/reports", AdminAPIController, :reports_update)
|
||||
post("/reports/:id/respond", AdminAPIController, :report_respond)
|
||||
post("/reports/:id/notes", AdminAPIController, :report_notes_create)
|
||||
delete("/reports/:report_id/notes/:id", AdminAPIController, :report_notes_delete)
|
||||
|
||||
put("/statuses/:id", AdminAPIController, :status_update)
|
||||
delete("/statuses/:id", AdminAPIController, :status_delete)
|
||||
|
|
@ -528,7 +529,10 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
get("/users/:nickname/feed", Feed.FeedController, :feed)
|
||||
get("/users/:nickname", Feed.FeedController, :feed_redirect)
|
||||
end
|
||||
|
||||
scope "/", Pleroma.Web do
|
||||
pipe_through(:browser)
|
||||
get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue