Merge branch 'develop' into feature/custom-runtime-modules

This commit is contained in:
Egor Kislitsyn 2019-12-13 01:51:52 +07:00
commit 7528322f83
69 changed files with 1129 additions and 1264 deletions

View file

@ -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

View file

@ -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,

View 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

View file

@ -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

View file

@ -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()

View file

@ -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()

View file

@ -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

View file

@ -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

View file

@ -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

View 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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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}))

View 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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -7,7 +7,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
plug(
OAuthScopesPlug,
%{scopes: ["write"]}
%{scopes: ["write"], admin: true}
when action in [
:create,
:delete,

View file

@ -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