Merge develop branch upstream
This commit is contained in:
commit
5d279a22b1
113 changed files with 909 additions and 560 deletions
|
|
@ -96,6 +96,15 @@ defmodule Mix.Tasks.Pleroma.Database do
|
|||
)
|
||||
|> Repo.delete_all(timeout: :infinity)
|
||||
|
||||
prune_hashtags_query = """
|
||||
DELETE FROM hashtags AS ht
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM hashtags_objects hto
|
||||
WHERE ht.id = hto.hashtag_id)
|
||||
"""
|
||||
|
||||
Repo.query(prune_hashtags_query)
|
||||
|
||||
if Keyword.get(options, :vacuum) do
|
||||
Maintenance.vacuum("full")
|
||||
end
|
||||
|
|
|
|||
|
|
@ -313,13 +313,15 @@ defmodule Pleroma.Activity do
|
|||
|
||||
def delete_all_by_object_ap_id(_), do: nil
|
||||
|
||||
defp purge_web_resp_cache(%Activity{} = activity) do
|
||||
%{path: path} = URI.parse(activity.data["id"])
|
||||
@cachex.del(:web_resp_cache, path)
|
||||
defp purge_web_resp_cache(%Activity{data: %{"id" => id}} = activity) when is_binary(id) do
|
||||
with %{path: path} <- URI.parse(id) do
|
||||
@cachex.del(:web_resp_cache, path)
|
||||
end
|
||||
|
||||
activity
|
||||
end
|
||||
|
||||
defp purge_web_resp_cache(nil), do: nil
|
||||
defp purge_web_resp_cache(activity), do: activity
|
||||
|
||||
def follow_accepted?(
|
||||
%Activity{data: %{"type" => "Follow", "object" => followed_ap_id}} = activity
|
||||
|
|
|
|||
|
|
@ -168,7 +168,8 @@ defmodule Pleroma.ApplicationRequirements do
|
|||
check_filter(Pleroma.Upload.Filter.Mogrify, "mogrify"),
|
||||
check_filter(Pleroma.Upload.Filter.Mogrifun, "mogrify"),
|
||||
check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "mogrify"),
|
||||
check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "convert")
|
||||
check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "convert"),
|
||||
check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "ffprobe")
|
||||
]
|
||||
|
||||
preview_proxy_commands_status =
|
||||
|
|
|
|||
|
|
@ -3,21 +3,21 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Config.Loader do
|
||||
defp reject_keys,
|
||||
do: [
|
||||
Pleroma.Repo,
|
||||
Pleroma.Web.Endpoint,
|
||||
:env,
|
||||
:configurable_from_database,
|
||||
:database,
|
||||
:swarm
|
||||
]
|
||||
# These modules are only being used as keys here (for equality check),
|
||||
# so it's okay to use `Module.concat/1` to have the compiler ignore them.
|
||||
@reject_keys [
|
||||
Module.concat(["Pleroma.Repo"]),
|
||||
Module.concat(["Pleroma.Web.Endpoint"]),
|
||||
:env,
|
||||
:configurable_from_database,
|
||||
:database,
|
||||
:swarm
|
||||
]
|
||||
|
||||
defp reject_groups,
|
||||
do: [
|
||||
:postgrex,
|
||||
:tesla
|
||||
]
|
||||
@reject_groups [
|
||||
:postgrex,
|
||||
:tesla
|
||||
]
|
||||
|
||||
if Code.ensure_loaded?(Config.Reader) do
|
||||
@reader Config.Reader
|
||||
|
|
@ -54,7 +54,7 @@ defmodule Pleroma.Config.Loader do
|
|||
@spec filter_group(atom(), keyword()) :: keyword()
|
||||
def filter_group(group, configs) do
|
||||
Enum.reject(configs[group], fn {key, _v} ->
|
||||
key in reject_keys() or group in reject_groups() or
|
||||
key in @reject_keys or group in @reject_groups or
|
||||
(group == :phoenix and key == :serve_endpoints)
|
||||
end)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,13 +5,18 @@
|
|||
defmodule Pleroma.Instances do
|
||||
@moduledoc "Instances context."
|
||||
|
||||
@adapter Pleroma.Instances.Instance
|
||||
alias Pleroma.Instances.Instance
|
||||
|
||||
defdelegate filter_reachable(urls_or_hosts), to: @adapter
|
||||
defdelegate reachable?(url_or_host), to: @adapter
|
||||
defdelegate set_reachable(url_or_host), to: @adapter
|
||||
defdelegate set_unreachable(url_or_host, unreachable_since \\ nil), to: @adapter
|
||||
defdelegate get_consistently_unreachable(), to: @adapter
|
||||
def filter_reachable(urls_or_hosts), do: Instance.filter_reachable(urls_or_hosts)
|
||||
|
||||
def reachable?(url_or_host), do: Instance.reachable?(url_or_host)
|
||||
|
||||
def set_reachable(url_or_host), do: Instance.set_reachable(url_or_host)
|
||||
|
||||
def set_unreachable(url_or_host, unreachable_since \\ nil),
|
||||
do: Instance.set_unreachable(url_or_host, unreachable_since)
|
||||
|
||||
def get_consistently_unreachable, do: Instance.get_consistently_unreachable()
|
||||
|
||||
def set_consistently_unreachable(url_or_host),
|
||||
do: set_unreachable(url_or_host, reachability_datetime_threshold())
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ defmodule Pleroma.Repo do
|
|||
adapter: Ecto.Adapters.Postgres,
|
||||
migration_timestamps: [type: :naive_datetime_usec]
|
||||
|
||||
use Ecto.Explain
|
||||
|
||||
import Ecto.Query
|
||||
require Logger
|
||||
|
||||
|
|
|
|||
|
|
@ -411,7 +411,7 @@ defmodule Pleroma.ReverseProxy do
|
|||
{:ok, :no_duration_limit, :no_duration_limit}
|
||||
end
|
||||
|
||||
defp client, do: Pleroma.ReverseProxy.Client
|
||||
defp client, do: Pleroma.ReverseProxy.Client.Wrapper
|
||||
|
||||
defp track_failed_url(url, error, opts) do
|
||||
ttl =
|
||||
|
|
|
|||
|
|
@ -17,22 +17,4 @@ defmodule Pleroma.ReverseProxy.Client do
|
|||
@callback stream_body(map()) :: {:ok, binary(), map()} | :done | {:error, atom() | String.t()}
|
||||
|
||||
@callback close(reference() | pid() | map()) :: :ok
|
||||
|
||||
def request(method, url, headers, body \\ "", opts \\ []) do
|
||||
client().request(method, url, headers, body, opts)
|
||||
end
|
||||
|
||||
def stream_body(ref), do: client().stream_body(ref)
|
||||
|
||||
def close(ref), do: client().close(ref)
|
||||
|
||||
defp client do
|
||||
:tesla
|
||||
|> Application.get_env(:adapter)
|
||||
|> client()
|
||||
end
|
||||
|
||||
defp client(Tesla.Adapter.Hackney), do: Pleroma.ReverseProxy.Client.Hackney
|
||||
defp client(Tesla.Adapter.Gun), do: Pleroma.ReverseProxy.Client.Tesla
|
||||
defp client(_), do: Pleroma.Config.get!(Pleroma.ReverseProxy.Client)
|
||||
end
|
||||
|
|
|
|||
29
lib/pleroma/reverse_proxy/client/wrapper.ex
Normal file
29
lib/pleroma/reverse_proxy/client/wrapper.ex
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.ReverseProxy.Client.Wrapper do
|
||||
@moduledoc "Meta-client that calls the appropriate client from the config."
|
||||
@behaviour Pleroma.ReverseProxy.Client
|
||||
|
||||
@impl true
|
||||
def request(method, url, headers, body \\ "", opts \\ []) do
|
||||
client().request(method, url, headers, body, opts)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def stream_body(ref), do: client().stream_body(ref)
|
||||
|
||||
@impl true
|
||||
def close(ref), do: client().close(ref)
|
||||
|
||||
defp client do
|
||||
:tesla
|
||||
|> Application.get_env(:adapter)
|
||||
|> client()
|
||||
end
|
||||
|
||||
defp client(Tesla.Adapter.Hackney), do: Pleroma.ReverseProxy.Client.Hackney
|
||||
defp client(Tesla.Adapter.Gun), do: Pleroma.ReverseProxy.Client.Tesla
|
||||
defp client(_), do: Pleroma.Config.get!(Pleroma.ReverseProxy.Client)
|
||||
end
|
||||
|
|
@ -9,7 +9,6 @@ defmodule Pleroma.Tests.AuthTestController do
|
|||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
|
||||
# Serves only with proper OAuth token (:api and :authenticated_api)
|
||||
|
|
@ -47,10 +46,7 @@ defmodule Pleroma.Tests.AuthTestController do
|
|||
# Via :authenticated_api, serves if token is present and has requested scopes
|
||||
#
|
||||
# Suggested use: as :fallback_oauth_check but open with nil :user for :api on private instances
|
||||
plug(
|
||||
:skip_plug,
|
||||
EnsurePublicOrAuthenticatedPlug when action == :fallback_oauth_skip_publicity_check
|
||||
)
|
||||
plug(:skip_public_check when action == :fallback_oauth_skip_publicity_check)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
|
|
@ -62,11 +58,7 @@ defmodule Pleroma.Tests.AuthTestController do
|
|||
# Via :authenticated_api, serves if :user is set (regardless of token presence and its scopes)
|
||||
#
|
||||
# Suggested use: making an :api endpoint always accessible (e.g. email confirmation endpoint)
|
||||
plug(
|
||||
:skip_plug,
|
||||
[OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug]
|
||||
when action == :skip_oauth_skip_publicity_check
|
||||
)
|
||||
plug(:skip_auth when action == :skip_oauth_skip_publicity_check)
|
||||
|
||||
# Via :authenticated_api, always fails with 403 (endpoint is insecure)
|
||||
# Via :api, drops :user if present and serves if public (private instance rejects on no user)
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@ defmodule Pleroma.Upload.Filter do
|
|||
|
||||
require Logger
|
||||
|
||||
@callback filter(Pleroma.Upload.t()) ::
|
||||
@callback filter(upload :: struct()) ::
|
||||
{:ok, :filtered}
|
||||
| {:ok, :noop}
|
||||
| {:ok, :filtered, Pleroma.Upload.t()}
|
||||
| {:ok, :filtered, upload :: struct()}
|
||||
| {:error, any()}
|
||||
|
||||
@spec filter([module()], Pleroma.Upload.t()) :: {:ok, Pleroma.Upload.t()} | {:error, any()}
|
||||
@spec filter([module()], upload :: struct()) :: {:ok, upload :: struct()} | {:error, any()}
|
||||
|
||||
def filter([], upload) do
|
||||
{:ok, upload}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,23 @@ defmodule Pleroma.Upload.Filter.AnalyzeMetadata do
|
|||
end
|
||||
end
|
||||
|
||||
def filter(%Pleroma.Upload{tempfile: file, content_type: "video" <> _} = upload) do
|
||||
try do
|
||||
result = media_dimensions(file)
|
||||
|
||||
upload =
|
||||
upload
|
||||
|> Map.put(:width, result.width)
|
||||
|> Map.put(:height, result.height)
|
||||
|
||||
{:ok, :filtered, upload}
|
||||
rescue
|
||||
e in ErlangError ->
|
||||
Logger.warn("#{__MODULE__}: #{inspect(e)}")
|
||||
{:ok, :noop}
|
||||
end
|
||||
end
|
||||
|
||||
def filter(_), do: {:ok, :noop}
|
||||
|
||||
defp get_blurhash(file) do
|
||||
|
|
@ -42,4 +59,25 @@ defmodule Pleroma.Upload.Filter.AnalyzeMetadata do
|
|||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
defp media_dimensions(file) do
|
||||
with executable when is_binary(executable) <- System.find_executable("ffprobe"),
|
||||
args = [
|
||||
"-v",
|
||||
"error",
|
||||
"-show_entries",
|
||||
"stream=width,height",
|
||||
"-of",
|
||||
"csv=p=0:s=x",
|
||||
file
|
||||
],
|
||||
{result, 0} <- System.cmd(executable, args),
|
||||
[width, height] <-
|
||||
String.split(String.trim(result), "x") |> Enum.map(&String.to_integer(&1)) do
|
||||
%{width: width, height: height}
|
||||
else
|
||||
nil -> {:error, {:ffprobe, :command_not_found}}
|
||||
{:error, _} = error -> error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1694,8 +1694,6 @@ defmodule Pleroma.User do
|
|||
email: nil,
|
||||
name: nil,
|
||||
password_hash: nil,
|
||||
keys: nil,
|
||||
public_key: nil,
|
||||
avatar: %{},
|
||||
tags: [],
|
||||
last_refreshed_at: nil,
|
||||
|
|
@ -1706,9 +1704,7 @@ defmodule Pleroma.User do
|
|||
follower_count: 0,
|
||||
following_count: 0,
|
||||
is_locked: false,
|
||||
is_confirmed: true,
|
||||
password_reset_pending: false,
|
||||
is_approved: true,
|
||||
registration_reason: nil,
|
||||
confirmation_token: nil,
|
||||
domain_blocks: [],
|
||||
|
|
@ -1723,45 +1719,53 @@ defmodule Pleroma.User do
|
|||
raw_fields: [],
|
||||
is_discoverable: false,
|
||||
also_known_as: []
|
||||
# id: preserved
|
||||
# ap_id: preserved
|
||||
# nickname: preserved
|
||||
})
|
||||
end
|
||||
|
||||
# Purge doesn't delete the user from the database.
|
||||
# It just nulls all its fields and deactivates it.
|
||||
# See `User.purge_user_changeset/1` above.
|
||||
defp purge(%User{} = user) do
|
||||
user
|
||||
|> purge_user_changeset()
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
def delete(users) when is_list(users) do
|
||||
for user <- users, do: delete(user)
|
||||
end
|
||||
|
||||
def delete(%User{} = user) do
|
||||
# Purge the user immediately
|
||||
purge(user)
|
||||
BackgroundWorker.enqueue("delete_user", %{"user_id" => user.id})
|
||||
end
|
||||
|
||||
defp delete_and_invalidate_cache(%User{} = user) do
|
||||
# *Actually* delete the user from the DB
|
||||
defp delete_from_db(%User{} = user) do
|
||||
invalidate_cache(user)
|
||||
Repo.delete(user)
|
||||
end
|
||||
|
||||
defp delete_or_deactivate(%User{local: false} = user), do: delete_and_invalidate_cache(user)
|
||||
# If the user never finalized their account, it's safe to delete them.
|
||||
defp maybe_delete_from_db(%User{local: true, is_confirmed: false} = user),
|
||||
do: delete_from_db(user)
|
||||
|
||||
defp delete_or_deactivate(%User{local: true} = user) do
|
||||
status = account_status(user)
|
||||
defp maybe_delete_from_db(%User{local: true, is_approved: false} = user),
|
||||
do: delete_from_db(user)
|
||||
|
||||
case status do
|
||||
:confirmation_pending ->
|
||||
delete_and_invalidate_cache(user)
|
||||
|
||||
:approval_pending ->
|
||||
delete_and_invalidate_cache(user)
|
||||
|
||||
_ ->
|
||||
user
|
||||
|> purge_user_changeset()
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
end
|
||||
defp maybe_delete_from_db(user), do: {:ok, user}
|
||||
|
||||
def perform(:force_password_reset, user), do: force_password_reset(user)
|
||||
|
||||
@spec perform(atom(), User.t()) :: {:ok, User.t()}
|
||||
def perform(:delete, %User{} = user) do
|
||||
# Purge the user again, in case perform/2 is called directly
|
||||
purge(user)
|
||||
|
||||
# Remove all relationships
|
||||
user
|
||||
|> get_followers()
|
||||
|
|
@ -1779,10 +1783,9 @@ defmodule Pleroma.User do
|
|||
|
||||
delete_user_activities(user)
|
||||
delete_notifications_from_user_activities(user)
|
||||
|
||||
delete_outgoing_pending_follow_requests(user)
|
||||
|
||||
delete_or_deactivate(user)
|
||||
maybe_delete_from_db(user)
|
||||
end
|
||||
|
||||
def perform(:set_activation_async, user, status), do: set_activation(user, status)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ defmodule Pleroma.User.Query do
|
|||
- e.g. Pleroma.User.Query.build(%{ap_id: ["http://ap_id1", "http://ap_id2"]})
|
||||
"""
|
||||
import Ecto.Query
|
||||
import Pleroma.Web.AdminAPI.Search, only: [not_empty_string: 1]
|
||||
import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1]
|
||||
|
||||
alias Pleroma.FollowingRelationship
|
||||
alias Pleroma.User
|
||||
|
|
|
|||
|
|
@ -62,6 +62,14 @@ defmodule Pleroma.Web do
|
|||
)
|
||||
end
|
||||
|
||||
defp skip_auth(conn, _) do
|
||||
skip_plug(conn, [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug])
|
||||
end
|
||||
|
||||
defp skip_public_check(conn, _) do
|
||||
skip_plug(conn, EnsurePublicOrAuthenticatedPlug)
|
||||
end
|
||||
|
||||
# Executed just before actual controller action, invokes before-action hooks (callbacks)
|
||||
defp action(conn, params) do
|
||||
with %{halted: false} = conn <-
|
||||
|
|
|
|||
|
|
@ -53,15 +53,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
{recipients, to, cc}
|
||||
end
|
||||
|
||||
defp check_actor_is_active(nil), do: true
|
||||
defp check_actor_can_insert(%{"type" => "Delete"}), do: true
|
||||
defp check_actor_can_insert(%{"type" => "Undo"}), do: true
|
||||
|
||||
defp check_actor_is_active(actor) when is_binary(actor) do
|
||||
defp check_actor_can_insert(%{"actor" => actor}) when is_binary(actor) do
|
||||
case User.get_cached_by_ap_id(actor) do
|
||||
%User{is_active: true} -> true
|
||||
_ -> false
|
||||
end
|
||||
end
|
||||
|
||||
defp check_actor_can_insert(_), do: true
|
||||
|
||||
defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do
|
||||
limit = Config.get([:instance, :remote_limit])
|
||||
String.length(content) <= limit
|
||||
|
|
@ -117,7 +120,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
|
||||
with nil <- Activity.normalize(map),
|
||||
map <- lazy_put_activity_defaults(map, fake),
|
||||
{_, true} <- {:actor_check, bypass_actor_check || check_actor_is_active(map["actor"])},
|
||||
{_, true} <- {:actor_check, bypass_actor_check || check_actor_can_insert(map)},
|
||||
{_, true} <- {:remote_limit_pass, check_remote_limit(map)},
|
||||
{:ok, map} <- MRF.filter(map),
|
||||
{recipients, _, _} = get_recipients(map),
|
||||
|
|
|
|||
|
|
@ -51,17 +51,6 @@ defmodule Pleroma.Web.ActivityPub.MRF do
|
|||
|
||||
@required_description_keys [:key, :related_policy]
|
||||
|
||||
@callback filter(Map.t()) :: {:ok | :reject, Map.t()}
|
||||
@callback describe() :: {:ok | :error, Map.t()}
|
||||
@callback config_description() :: %{
|
||||
optional(:children) => [map()],
|
||||
key: atom(),
|
||||
related_policy: String.t(),
|
||||
label: String.t(),
|
||||
description: String.t()
|
||||
}
|
||||
@optional_callbacks config_description: 0
|
||||
|
||||
def filter(policies, %{} = message) do
|
||||
policies
|
||||
|> Enum.reduce({:ok, message}, fn
|
||||
|
|
@ -142,7 +131,7 @@ defmodule Pleroma.Web.ActivityPub.MRF do
|
|||
def describe, do: get_policies() |> describe()
|
||||
|
||||
def config_descriptions do
|
||||
Pleroma.Web.ActivityPub.MRF
|
||||
Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|> Pleroma.Docs.Generator.list_behaviour_implementations()
|
||||
|> config_descriptions()
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy do
|
||||
@moduledoc "Adds expiration to all local Create activities"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
@impl true
|
||||
def filter(activity) do
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do
|
|||
|
||||
@moduledoc "Prevent followbots from following with a bit of heuristic"
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
# XXX: this should become User.normalize_by_ap_id() or similar, really.
|
||||
defp normalize_by_ap_id(%{"id" => id}), do: User.get_cached_by_ap_id(id)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do
|
||||
alias Pleroma.User
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
require Logger
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do
|
||||
require Logger
|
||||
@moduledoc "Drop and log everything received"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
@impl true
|
||||
def filter(object) do
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do
|
|||
alias Pleroma.Object
|
||||
|
||||
@moduledoc "Ensure a re: is prepended on replies to a post with a Subject"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
@reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless])
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
defmodule Pleroma.Web.ActivityPub.MRF.FollowBotPolicy do
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy do
|
||||
alias Pleroma.User
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
@moduledoc "Remove bot posts from federated timeline"
|
||||
|
||||
require Pleroma.Constants
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do
|
|||
Note: This MRF Policy is always enabled, if you want to disable it you have to set empty lists.
|
||||
"""
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
defp check_reject(message, hashtags) do
|
||||
if Enum.any?(Config.get([:mrf_hashtag, :reject]), fn match -> match in hashtags end) do
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
|
|||
|
||||
@moduledoc "Block messages with too much mentions (configurable)"
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
defp delist_message(message, threshold) when threshold > 0 do
|
||||
follower_collection = User.get_cached_by_ap_id(message["actor"]).follower_address
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
|
|||
|
||||
@moduledoc "Reject or Word-Replace messages with a keyword or regex"
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
defp string_matches?(string, _) when not is_binary(string) do
|
||||
false
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
|
||||
@moduledoc "Preloads any attachments in the MediaProxy cache by prefetching them"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
alias Pleroma.HTTP
|
||||
alias Pleroma.Web.MediaProxy
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicy do
|
||||
@moduledoc "Block messages which mention a user"
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
@impl true
|
||||
def filter(%{"type" => "Create"} = message) do
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do
|
||||
@moduledoc "Filter local activities which have no content"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
alias Pleroma.Web.Endpoint
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do
|
||||
@moduledoc "Does nothing (lets the messages go through unmodified)"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
@impl true
|
||||
def filter(object) do
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy do
|
||||
@moduledoc "Ensure no content placeholder is present (such as the dot from mastodon)"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
@impl true
|
||||
def filter(
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do
|
|||
@moduledoc "Scrub configured hypertext markup"
|
||||
alias Pleroma.HTML
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
@impl true
|
||||
def filter(%{"type" => "Create", "object" => child_object} = object) do
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
|
|||
require Pleroma.Constants
|
||||
|
||||
@moduledoc "Filter activities depending on their age"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
defp check_date(%{"object" => %{"published" => published}} = message) do
|
||||
with %DateTime{} = now <- DateTime.utc_now(),
|
||||
|
|
|
|||
16
lib/pleroma/web/activity_pub/mrf/policy.ex
Normal file
16
lib/pleroma/web/activity_pub/mrf/policy.ex
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.Policy do
|
||||
@callback filter(Map.t()) :: {:ok | :reject, Map.t()}
|
||||
@callback describe() :: {:ok | :error, Map.t()}
|
||||
@callback config_description() :: %{
|
||||
optional(:children) => [map()],
|
||||
key: atom(),
|
||||
related_policy: String.t(),
|
||||
label: String.t(),
|
||||
description: String.t()
|
||||
}
|
||||
@optional_callbacks config_description: 0
|
||||
end
|
||||
|
|
@ -8,7 +8,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
|
|||
alias Pleroma.Config
|
||||
alias Pleroma.User
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
require Pleroma.Constants
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
||||
@moduledoc "Filter activities depending on their origin instance"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.FollowingRelationship
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do
|
|||
alias Pleroma.Config
|
||||
|
||||
@moduledoc "Detect new emojis by their shortcode and steals them"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
defp accept_host?(host), do: host in Config.get([:mrf_steal_emoji, :hosts], [])
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do
|
|||
|
||||
require Logger
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
defp lookup_subchain(actor) do
|
||||
with matches <- Config.get([:mrf_subchain, :match_actor]),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
|
||||
alias Pleroma.User
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
@moduledoc """
|
||||
Apply policies based on user tags
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
|
|||
alias Pleroma.Config
|
||||
|
||||
@moduledoc "Accept-list of users from specified instances"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
defp filter_by_list(object, []), do: {:ok, object}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do
|
||||
@moduledoc "Filter messages which belong to certain activity vocabularies"
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
@impl true
|
||||
def filter(%{"type" => "Undo", "object" => child_message} = message) do
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
|
|||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
|
||||
|
|
@ -23,7 +24,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
|
|||
field(:type, :string)
|
||||
field(:object, ObjectValidators.ObjectID)
|
||||
field(:actor, ObjectValidators.ObjectID)
|
||||
field(:context, :string, autogenerate: {Utils, :generate_context_id, []})
|
||||
field(:context, :string)
|
||||
field(:to, ObjectValidators.Recipients, default: [])
|
||||
field(:cc, ObjectValidators.Recipients, default: [])
|
||||
field(:published, ObjectValidators.DateTime)
|
||||
|
|
@ -36,6 +37,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
|
|||
end
|
||||
|
||||
def cast_data(data) do
|
||||
data =
|
||||
data
|
||||
|> fix()
|
||||
|
||||
%__MODULE__{}
|
||||
|> changeset(data)
|
||||
end
|
||||
|
|
@ -43,11 +48,21 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
|
|||
def changeset(struct, data) do
|
||||
struct
|
||||
|> cast(data, __schema__(:fields))
|
||||
|> fix_after_cast()
|
||||
end
|
||||
|
||||
def fix_after_cast(cng) do
|
||||
cng
|
||||
defp fix(data) do
|
||||
data =
|
||||
data
|
||||
|> CommonFixes.fix_actor()
|
||||
|> CommonFixes.fix_activity_addressing()
|
||||
|
||||
with %Object{} = object <- Object.normalize(data["object"]) do
|
||||
data
|
||||
|> CommonFixes.fix_activity_context(object)
|
||||
|> CommonFixes.fix_object_action_recipients(object)
|
||||
else
|
||||
_ -> data
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_data(data_cng) do
|
||||
|
|
@ -60,7 +75,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
|
|||
|> validate_announcable()
|
||||
end
|
||||
|
||||
def validate_announcable(cng) do
|
||||
defp validate_announcable(cng) do
|
||||
with actor when is_binary(actor) <- get_field(cng, :actor),
|
||||
object when is_binary(object) <- get_field(cng, :object),
|
||||
%User{} = actor <- User.get_cached_by_ap_id(actor),
|
||||
|
|
@ -91,7 +106,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
|
|||
end
|
||||
end
|
||||
|
||||
def validate_existing_announce(cng) do
|
||||
defp validate_existing_announce(cng) do
|
||||
actor = get_field(cng, :actor)
|
||||
object = get_field(cng, :object)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Object.Containment
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
|
|
@ -36,7 +37,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
|
|||
|> Transmogrifier.fix_implicit_addressing(follower_collection)
|
||||
end
|
||||
|
||||
def fix_activity_addressing(activity, _meta) do
|
||||
def fix_activity_addressing(activity) do
|
||||
%User{follower_address: follower_collection} = User.get_cached_by_ap_id(activity["actor"])
|
||||
|
||||
activity
|
||||
|
|
@ -57,4 +58,21 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
|
|||
|> Map.put("actor", actor)
|
||||
|> Map.put("attributedTo", actor)
|
||||
end
|
||||
|
||||
def fix_activity_context(data, %Object{data: %{"context" => object_context}}) do
|
||||
data
|
||||
|> Map.put("context", object_context)
|
||||
end
|
||||
|
||||
def fix_object_action_recipients(%{"actor" => actor} = data, %Object{data: %{"actor" => actor}}) do
|
||||
to = ((data["to"] || []) -- [actor]) |> Enum.uniq()
|
||||
|
||||
Map.put(data, "to", to)
|
||||
end
|
||||
|
||||
def fix_object_action_recipients(data, %Object{data: %{"actor" => actor}}) do
|
||||
to = ((data["to"] || []) ++ [actor]) |> Enum.uniq()
|
||||
|
||||
Map.put(data, "to", to)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator do
|
|||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||
alias Pleroma.User
|
||||
|
||||
import Ecto.Changeset
|
||||
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||
|
|
@ -57,7 +58,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator do
|
|||
cng
|
||||
|> validate_required([:id, :type, :actor, :to, :cc, :object])
|
||||
|> validate_inclusion(:type, ["Delete"])
|
||||
|> validate_actor_presence()
|
||||
|> validate_delete_actor(:actor)
|
||||
|> validate_modification_rights()
|
||||
|> validate_object_or_user_presence(allowed_types: @deletable_types)
|
||||
|> add_deleted_activity_id()
|
||||
|
|
@ -72,4 +73,13 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator do
|
|||
|> cast_data
|
||||
|> validate_data
|
||||
end
|
||||
|
||||
defp validate_delete_actor(cng, field_name) do
|
||||
validate_change(cng, field_name, fn field_name, actor ->
|
||||
case User.get_cached_by_ap_id(actor) do
|
||||
%User{} -> []
|
||||
_ -> [{field_name, "can't find user"}]
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do
|
|||
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
||||
|
||||
import Ecto.Changeset
|
||||
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||
|
|
@ -31,6 +32,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do
|
|||
end
|
||||
|
||||
def cast_data(data) do
|
||||
data =
|
||||
data
|
||||
|> fix()
|
||||
|
||||
%__MODULE__{}
|
||||
|> changeset(data)
|
||||
end
|
||||
|
|
@ -38,28 +43,24 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do
|
|||
def changeset(struct, data) do
|
||||
struct
|
||||
|> cast(data, __schema__(:fields))
|
||||
|> fix_after_cast()
|
||||
end
|
||||
|
||||
def fix_after_cast(cng) do
|
||||
cng
|
||||
|> fix_context()
|
||||
end
|
||||
defp fix(data) do
|
||||
data =
|
||||
data
|
||||
|> CommonFixes.fix_actor()
|
||||
|> CommonFixes.fix_activity_addressing()
|
||||
|
||||
def fix_context(cng) do
|
||||
object = get_field(cng, :object)
|
||||
|
||||
with nil <- get_field(cng, :context),
|
||||
%Object{data: %{"context" => context}} <- Object.get_cached_by_ap_id(object) do
|
||||
cng
|
||||
|> put_change(:context, context)
|
||||
with %Object{} = object <- Object.normalize(data["object"]) do
|
||||
data
|
||||
|> CommonFixes.fix_activity_context(object)
|
||||
|> CommonFixes.fix_object_action_recipients(object)
|
||||
else
|
||||
_ ->
|
||||
cng
|
||||
_ -> data
|
||||
end
|
||||
end
|
||||
|
||||
def validate_emoji(cng) do
|
||||
defp validate_emoji(cng) do
|
||||
content = get_field(cng, :content)
|
||||
|
||||
if Pleroma.Emoji.is_unicode_emoji?(content) do
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do
|
|||
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
|
||||
import Ecto.Changeset
|
||||
|
|
@ -31,6 +32,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do
|
|||
end
|
||||
|
||||
def cast_data(data) do
|
||||
data =
|
||||
data
|
||||
|> fix()
|
||||
|
||||
%__MODULE__{}
|
||||
|> changeset(data)
|
||||
end
|
||||
|
|
@ -38,41 +43,20 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do
|
|||
def changeset(struct, data) do
|
||||
struct
|
||||
|> cast(data, __schema__(:fields))
|
||||
|> fix_after_cast()
|
||||
end
|
||||
|
||||
def fix_after_cast(cng) do
|
||||
cng
|
||||
|> fix_recipients()
|
||||
|> fix_context()
|
||||
end
|
||||
defp fix(data) do
|
||||
data =
|
||||
data
|
||||
|> CommonFixes.fix_actor()
|
||||
|> CommonFixes.fix_activity_addressing()
|
||||
|
||||
def fix_context(cng) do
|
||||
object = get_field(cng, :object)
|
||||
|
||||
with nil <- get_field(cng, :context),
|
||||
%Object{data: %{"context" => context}} <- Object.get_cached_by_ap_id(object) do
|
||||
cng
|
||||
|> put_change(:context, context)
|
||||
with %Object{} = object <- Object.normalize(data["object"]) do
|
||||
data
|
||||
|> CommonFixes.fix_activity_context(object)
|
||||
|> CommonFixes.fix_object_action_recipients(object)
|
||||
else
|
||||
_ ->
|
||||
cng
|
||||
end
|
||||
end
|
||||
|
||||
def fix_recipients(cng) do
|
||||
to = get_field(cng, :to)
|
||||
cc = get_field(cng, :cc)
|
||||
object = get_field(cng, :object)
|
||||
|
||||
with {[], []} <- {to, cc},
|
||||
%Object{data: %{"actor" => actor}} <- Object.get_cached_by_ap_id(object),
|
||||
{:ok, actor} <- ObjectValidators.ObjectID.cast(actor) do
|
||||
cng
|
||||
|> put_change(:to, [actor])
|
||||
else
|
||||
_ ->
|
||||
cng
|
||||
_ -> data
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -85,7 +69,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do
|
|||
|> validate_existing_like()
|
||||
end
|
||||
|
||||
def validate_existing_like(%{changes: %{actor: actor, object: object}} = cng) do
|
||||
defp validate_existing_like(%{changes: %{actor: actor, object: object}} = cng) do
|
||||
if Utils.get_existing_like(actor, %{data: %{"id" => object}}) do
|
||||
cng
|
||||
|> add_error(:actor, "already liked this object")
|
||||
|
|
@ -95,5 +79,5 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do
|
|||
end
|
||||
end
|
||||
|
||||
def validate_existing_like(cng), do: cng
|
||||
defp validate_existing_like(cng), do: cng
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator do
|
|||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||
alias Pleroma.User
|
||||
|
||||
import Ecto.Changeset
|
||||
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||
|
|
@ -42,7 +43,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator do
|
|||
data_cng
|
||||
|> validate_inclusion(:type, ["Undo"])
|
||||
|> validate_required([:id, :type, :object, :actor, :to, :cc])
|
||||
|> validate_actor_presence()
|
||||
|> validate_undo_actor(:actor)
|
||||
|> validate_object_presence()
|
||||
|> validate_undo_rights()
|
||||
end
|
||||
|
|
@ -59,4 +60,13 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator do
|
|||
_ -> cng
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_undo_actor(cng, field_name) do
|
||||
validate_change(cng, field_name, fn field_name, actor ->
|
||||
case User.get_cached_by_ap_id(actor) do
|
||||
%User{} -> []
|
||||
_ -> [{field_name, "can't find user"}]
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -28,11 +28,12 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
require Logger
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
@ap_streamer Pleroma.Config.get([:side_effects, :ap_streamer], ActivityPub)
|
||||
@logger Pleroma.Config.get([:side_effects, :logger], Logger)
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.SideEffects.Handling
|
||||
|
||||
defp ap_streamer, do: Pleroma.Config.get([:side_effects, :ap_streamer], ActivityPub)
|
||||
|
||||
@impl true
|
||||
def handle(object, meta \\ [])
|
||||
|
||||
|
|
@ -302,8 +303,8 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
|
||||
MessageReference.delete_for_object(deleted_object)
|
||||
|
||||
@ap_streamer.stream_out(object)
|
||||
@ap_streamer.stream_out_participations(deleted_object, user)
|
||||
ap_streamer().stream_out(object)
|
||||
ap_streamer().stream_out_participations(deleted_object, user)
|
||||
:ok
|
||||
else
|
||||
{:actor, _} ->
|
||||
|
|
|
|||
|
|
@ -45,8 +45,6 @@ defmodule Pleroma.Web.AdminAPI.UserController do
|
|||
when action in [:follow, :unfollow]
|
||||
)
|
||||
|
||||
plug(:put_view, Pleroma.Web.AdminAPI.AccountView)
|
||||
|
||||
action_fallback(AdminAPI.FallbackController)
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.UserOperation
|
||||
|
|
|
|||
|
|
@ -10,12 +10,6 @@ defmodule Pleroma.Web.AdminAPI.Search do
|
|||
|
||||
@page_size 50
|
||||
|
||||
defmacro not_empty_string(string) do
|
||||
quote do
|
||||
is_binary(unquote(string)) and unquote(string) != ""
|
||||
end
|
||||
end
|
||||
|
||||
@spec user(map()) :: {:ok, [User.t()], pos_integer()}
|
||||
def user(params \\ %{}) do
|
||||
query =
|
||||
|
|
|
|||
10
lib/pleroma/web/admin_api/views/user_view.ex
Normal file
10
lib/pleroma/web/admin_api/views/user_view.ex
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.AdminAPI.UserView do
|
||||
use Pleroma.Web, :view
|
||||
alias Pleroma.Web.AdminAPI
|
||||
|
||||
def render(view, opts), do: AdminAPI.AccountView.render(view, opts)
|
||||
end
|
||||
|
|
@ -24,6 +24,7 @@ defmodule Pleroma.Web.ApiSpec.MediaOperation do
|
|||
requestBody: Helpers.request_body("Parameters", create_request()),
|
||||
responses: %{
|
||||
200 => Operation.response("Media", "application/json", Attachment),
|
||||
400 => Operation.response("Media", "application/json", ApiError),
|
||||
401 => Operation.response("Media", "application/json", ApiError),
|
||||
422 => Operation.response("Media", "application/json", ApiError)
|
||||
}
|
||||
|
|
@ -121,6 +122,7 @@ defmodule Pleroma.Web.ApiSpec.MediaOperation do
|
|||
requestBody: Helpers.request_body("Parameters", create_request()),
|
||||
responses: %{
|
||||
202 => Operation.response("Media", "application/json", Attachment),
|
||||
400 => Operation.response("Media", "application/json", ApiError),
|
||||
422 => Operation.response("Media", "application/json", ApiError),
|
||||
500 => Operation.response("Media", "application/json", ApiError)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.BooleanLike do
|
|||
|
||||
def cast(%Cast{value: value} = context) do
|
||||
context
|
||||
|> Map.put(:value, Pleroma.Web.ControllerHelper.truthy_param?(value))
|
||||
|> Map.put(:value, Pleroma.Web.Utils.Params.truthy_param?(value))
|
||||
|> Cast.ok()
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,68 +3,11 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Auth.Authenticator do
|
||||
alias Pleroma.Registration
|
||||
alias Pleroma.User
|
||||
|
||||
def implementation do
|
||||
Pleroma.Config.get(
|
||||
Pleroma.Web.Auth.Authenticator,
|
||||
Pleroma.Web.Auth.PleromaAuthenticator
|
||||
)
|
||||
end
|
||||
|
||||
@callback get_user(Plug.Conn.t()) :: {:ok, User.t()} | {:error, any()}
|
||||
def get_user(plug), do: implementation().get_user(plug)
|
||||
|
||||
@callback create_from_registration(Plug.Conn.t(), Registration.t()) ::
|
||||
@callback get_user(Plug.Conn.t()) :: {:ok, user :: struct()} | {:error, any()}
|
||||
@callback create_from_registration(Plug.Conn.t(), registration :: struct()) ::
|
||||
{:ok, User.t()} | {:error, any()}
|
||||
def create_from_registration(plug, registration),
|
||||
do: implementation().create_from_registration(plug, registration)
|
||||
|
||||
@callback get_registration(Plug.Conn.t()) :: {:ok, Registration.t()} | {:error, any()}
|
||||
def get_registration(plug), do: implementation().get_registration(plug)
|
||||
|
||||
@callback get_registration(Plug.Conn.t()) :: {:ok, registration :: struct()} | {:error, any()}
|
||||
@callback handle_error(Plug.Conn.t(), any()) :: any()
|
||||
def handle_error(plug, error),
|
||||
do: implementation().handle_error(plug, error)
|
||||
|
||||
@callback auth_template() :: String.t() | nil
|
||||
def auth_template do
|
||||
# Note: `config :pleroma, :auth_template, "..."` support is deprecated
|
||||
implementation().auth_template() ||
|
||||
Pleroma.Config.get([:auth, :auth_template], Pleroma.Config.get(:auth_template)) ||
|
||||
"show.html"
|
||||
end
|
||||
|
||||
@callback oauth_consumer_template() :: String.t() | nil
|
||||
def oauth_consumer_template do
|
||||
implementation().oauth_consumer_template() ||
|
||||
Pleroma.Config.get([:auth, :oauth_consumer_template], "consumer.html")
|
||||
end
|
||||
|
||||
@doc "Gets user by nickname or email for auth."
|
||||
@spec fetch_user(String.t()) :: User.t() | nil
|
||||
def fetch_user(name) do
|
||||
User.get_by_nickname_or_email(name)
|
||||
end
|
||||
|
||||
# Gets name and password from conn
|
||||
#
|
||||
@spec fetch_credentials(Plug.Conn.t() | map()) ::
|
||||
{:ok, {name :: any, password :: any}} | {:error, :invalid_credentials}
|
||||
def fetch_credentials(%Plug.Conn{params: params} = _),
|
||||
do: fetch_credentials(params)
|
||||
|
||||
def fetch_credentials(params) do
|
||||
case params do
|
||||
%{"authorization" => %{"name" => name, "password" => password}} ->
|
||||
{:ok, {name, password}}
|
||||
|
||||
%{"grant_type" => "password", "username" => name, "password" => password} ->
|
||||
{:ok, {name, password}}
|
||||
|
||||
_ ->
|
||||
{:error, :invalid_credentials}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
33
lib/pleroma/web/auth/helpers.ex
Normal file
33
lib/pleroma/web/auth/helpers.ex
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Auth.Helpers do
|
||||
alias Pleroma.User
|
||||
|
||||
@doc "Gets user by nickname or email for auth."
|
||||
@spec fetch_user(String.t()) :: User.t() | nil
|
||||
def fetch_user(name) do
|
||||
User.get_by_nickname_or_email(name)
|
||||
end
|
||||
|
||||
# Gets name and password from conn
|
||||
#
|
||||
@spec fetch_credentials(Plug.Conn.t() | map()) ::
|
||||
{:ok, {name :: any, password :: any}} | {:error, :invalid_credentials}
|
||||
def fetch_credentials(%Plug.Conn{params: params} = _),
|
||||
do: fetch_credentials(params)
|
||||
|
||||
def fetch_credentials(params) do
|
||||
case params do
|
||||
%{"authorization" => %{"name" => name, "password" => password}} ->
|
||||
{:ok, {name, password}}
|
||||
|
||||
%{"grant_type" => "password", "username" => name, "password" => password} ->
|
||||
{:ok, {name, password}}
|
||||
|
||||
_ ->
|
||||
{:error, :invalid_credentials}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -7,8 +7,7 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do
|
|||
|
||||
require Logger
|
||||
|
||||
import Pleroma.Web.Auth.Authenticator,
|
||||
only: [fetch_credentials: 1, fetch_user: 1]
|
||||
import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1, fetch_user: 1]
|
||||
|
||||
@behaviour Pleroma.Web.Auth.Authenticator
|
||||
@base Pleroma.Web.Auth.PleromaAuthenticator
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
|
|||
alias Pleroma.User
|
||||
alias Pleroma.Web.Plugs.AuthenticationPlug
|
||||
|
||||
import Pleroma.Web.Auth.Authenticator,
|
||||
only: [fetch_credentials: 1, fetch_user: 1]
|
||||
import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1, fetch_user: 1]
|
||||
|
||||
@behaviour Pleroma.Web.Auth.Authenticator
|
||||
|
||||
|
|
|
|||
42
lib/pleroma/web/auth/wrapper_authenticator.ex
Normal file
42
lib/pleroma/web/auth/wrapper_authenticator.ex
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Auth.WrapperAuthenticator do
|
||||
@behaviour Pleroma.Web.Auth.Authenticator
|
||||
|
||||
defp implementation do
|
||||
Pleroma.Config.get(
|
||||
Pleroma.Web.Auth.Authenticator,
|
||||
Pleroma.Web.Auth.PleromaAuthenticator
|
||||
)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def get_user(plug), do: implementation().get_user(plug)
|
||||
|
||||
@impl true
|
||||
def create_from_registration(plug, registration),
|
||||
do: implementation().create_from_registration(plug, registration)
|
||||
|
||||
@impl true
|
||||
def get_registration(plug), do: implementation().get_registration(plug)
|
||||
|
||||
@impl true
|
||||
def handle_error(plug, error),
|
||||
do: implementation().handle_error(plug, error)
|
||||
|
||||
@impl true
|
||||
def auth_template do
|
||||
# Note: `config :pleroma, :auth_template, "..."` support is deprecated
|
||||
implementation().auth_template() ||
|
||||
Pleroma.Config.get([:auth, :auth_template], Pleroma.Config.get(:auth_template)) ||
|
||||
"show.html"
|
||||
end
|
||||
|
||||
@impl true
|
||||
def oauth_consumer_template do
|
||||
implementation().oauth_consumer_template() ||
|
||||
Pleroma.Config.get([:auth, :oauth_consumer_template], "consumer.html")
|
||||
end
|
||||
end
|
||||
|
|
@ -223,7 +223,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
|||
end
|
||||
|
||||
defp preview?(draft) do
|
||||
preview? = Pleroma.Web.ControllerHelper.truthy_param?(draft.params[:preview])
|
||||
preview? = Pleroma.Web.Utils.Params.truthy_param?(draft.params[:preview])
|
||||
%__MODULE__{draft | preview?: preview?}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
defmodule Pleroma.Web.CommonAPI.Utils do
|
||||
import Pleroma.Web.Gettext
|
||||
import Pleroma.Web.ControllerHelper, only: [truthy_param?: 1]
|
||||
|
||||
alias Calendar.Strftime
|
||||
alias Pleroma.Activity
|
||||
|
|
@ -19,6 +18,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
alias Pleroma.Web.CommonAPI.ActivityDraft
|
||||
alias Pleroma.Web.MediaProxy
|
||||
alias Pleroma.Web.Plugs.AuthenticationPlug
|
||||
alias Pleroma.Web.Utils.Params
|
||||
|
||||
require Logger
|
||||
require Pleroma.Constants
|
||||
|
|
@ -160,7 +160,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
|> DateTime.add(expires_in)
|
||||
|> DateTime.to_iso8601()
|
||||
|
||||
key = if truthy_param?(data.poll[:multiple]), do: "anyOf", else: "oneOf"
|
||||
key = if Params.truthy_param?(data.poll[:multiple]), do: "anyOf", else: "oneOf"
|
||||
poll = %{"type" => "Question", key => option_notes, "closed" => end_time}
|
||||
|
||||
{:ok, {poll, emoji}}
|
||||
|
|
@ -203,7 +203,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
attachment_links =
|
||||
draft.params
|
||||
|> Map.get("attachment_links", Config.get([:instance, :attachment_links]))
|
||||
|> truthy_param?()
|
||||
|> Params.truthy_param?()
|
||||
|
||||
content_type = get_content_type(draft.params[:content_type])
|
||||
|
||||
|
|
|
|||
|
|
@ -6,17 +6,7 @@ defmodule Pleroma.Web.ControllerHelper do
|
|||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Pagination
|
||||
|
||||
# As in Mastodon API, per https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html
|
||||
@falsy_param_values [false, 0, "0", "f", "F", "false", "False", "FALSE", "off", "OFF"]
|
||||
|
||||
def explicitly_falsy_param?(value), do: value in @falsy_param_values
|
||||
|
||||
# Note: `nil` and `""` are considered falsy values in Pleroma
|
||||
def falsy_param?(value),
|
||||
do: explicitly_falsy_param?(value) or value in [nil, ""]
|
||||
|
||||
def truthy_param?(value), do: not falsy_param?(value)
|
||||
alias Pleroma.Web.Utils.Params
|
||||
|
||||
def json_response(conn, status, _) when status in [204, :no_content] do
|
||||
conn
|
||||
|
|
@ -123,6 +113,6 @@ defmodule Pleroma.Web.ControllerHelper do
|
|||
# To do once OpenAPI transition mess is over: just `truthy_param?(params[:with_relationships])`
|
||||
params
|
||||
|> Map.get(:with_relationships, params["with_relationships"])
|
||||
|> truthy_param?()
|
||||
|> Params.truthy_param?()
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
import Pleroma.Web.ControllerHelper,
|
||||
only: [
|
||||
add_link_headers: 2,
|
||||
truthy_param?: 1,
|
||||
assign_account_by_id: 2,
|
||||
embed_relationships?: 1,
|
||||
json_response: 3
|
||||
|
|
@ -25,16 +24,16 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
alias Pleroma.Web.MastodonAPI.MastodonAPIController
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
alias Pleroma.Web.OAuth.OAuthController
|
||||
alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Web.Plugs.RateLimiter
|
||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||
alias Pleroma.Web.Utils.Params
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
plug(:skip_plug, [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :create)
|
||||
plug(:skip_auth when action == :create)
|
||||
|
||||
plug(:skip_plug, EnsurePublicOrAuthenticatedPlug when action in [:show, :statuses])
|
||||
plug(:skip_public_check when action in [:show, :statuses])
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
|
|
@ -188,7 +187,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
:accepts_chat_messages
|
||||
]
|
||||
|> Enum.reduce(%{}, fn key, acc ->
|
||||
Maps.put_if_present(acc, key, params[key], &{:ok, truthy_param?(&1)})
|
||||
Maps.put_if_present(acc, key, params[key], &{:ok, Params.truthy_param?(&1)})
|
||||
end)
|
||||
|> Maps.put_if_present(:name, params[:display_name])
|
||||
|> Maps.put_if_present(:bio, params[:note])
|
||||
|
|
|
|||
|
|
@ -14,16 +14,10 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
|
|||
alias Pleroma.Web.OAuth.App
|
||||
alias Pleroma.Web.OAuth.Scopes
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
plug(
|
||||
:skip_plug,
|
||||
[OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug]
|
||||
when action in [:create, :verify_credentials]
|
||||
)
|
||||
plug(:skip_auth when action in [:create, :verify_credentials])
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,11 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.CustomEmojiController do
|
|||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
plug(
|
||||
:skip_plug,
|
||||
[Pleroma.Web.Plugs.OAuthScopesPlug, Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug]
|
||||
when action == :index
|
||||
)
|
||||
plug(:skip_auth when action == :index)
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.CustomEmojiOperation
|
||||
|
||||
|
|
|
|||
|
|
@ -7,11 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceController do
|
|||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
plug(
|
||||
:skip_plug,
|
||||
[Pleroma.Web.Plugs.OAuthScopesPlug, Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug]
|
||||
when action in [:show, :peers]
|
||||
)
|
||||
plug(:skip_auth when action in [:show, :peers])
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.InstanceOperation
|
||||
|
||||
|
|
|
|||
|
|
@ -15,11 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
|
||||
require Logger
|
||||
|
||||
plug(
|
||||
:skip_plug,
|
||||
[Pleroma.Web.Plugs.OAuthScopesPlug, Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug]
|
||||
when action in [:empty_array, :empty_object]
|
||||
)
|
||||
plug(:skip_auth when action in [:empty_array, :empty_object])
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
|
|
|
|||
|
|
@ -27,10 +27,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
plug(
|
||||
:skip_plug,
|
||||
Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug when action in [:index, :show]
|
||||
)
|
||||
plug(:skip_public_check when action in [:index, :show])
|
||||
|
||||
@unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,12 +12,11 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
|||
alias Pleroma.Pagination
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Web.Plugs.RateLimiter
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
plug(:skip_plug, EnsurePublicOrAuthenticatedPlug when action in [:public, :hashtag])
|
||||
plug(:skip_public_check when action in [:public, :hashtag])
|
||||
|
||||
# TODO: Replace with a macro when there is a Phoenix release with the following commit in it:
|
||||
# https://github.com/phoenixframework/phoenix/commit/2e8c63c01fec4dde5467dbbbf9705ff9e780735e
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.MediaProxy
|
||||
alias Pleroma.Web.Metadata
|
||||
alias Pleroma.Web.Metadata.Providers.Provider
|
||||
alias Pleroma.Web.Metadata.Utils
|
||||
|
|
@ -19,37 +20,24 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
|
|||
}) do
|
||||
attachments = build_attachments(object)
|
||||
scrubbed_content = Utils.scrub_html_and_truncate(object)
|
||||
# Zero width space
|
||||
content =
|
||||
if scrubbed_content != "" and scrubbed_content != "\u200B" do
|
||||
": “" <> scrubbed_content <> "”"
|
||||
else
|
||||
""
|
||||
end
|
||||
|
||||
# Most previews only show og:title which is inconvenient. Instagram
|
||||
# hacks this by putting the description in the title and making the
|
||||
# description longer prefixed by how many likes and shares the post
|
||||
# has. Here we use the descriptive nickname in the title, and expand
|
||||
# the full account & nickname in the description. We also use the cute^Wevil
|
||||
# smart quotes around the status text like Instagram, too.
|
||||
[
|
||||
{:meta,
|
||||
[
|
||||
property: "og:title",
|
||||
content: "#{user.name}" <> content
|
||||
content: Utils.user_name_string(user)
|
||||
], []},
|
||||
{:meta, [property: "og:url", content: url], []},
|
||||
{:meta,
|
||||
[
|
||||
property: "og:description",
|
||||
content: "#{Utils.user_name_string(user)}" <> content
|
||||
content: scrubbed_content
|
||||
], []},
|
||||
{:meta, [property: "og:type", content: "website"], []}
|
||||
{:meta, [property: "og:type", content: "article"], []}
|
||||
] ++
|
||||
if attachments == [] or Metadata.activity_nsfw?(object) do
|
||||
[
|
||||
{:meta, [property: "og:image", content: Utils.attachment_url(User.avatar_url(user))],
|
||||
{:meta, [property: "og:image", content: MediaProxy.preview_url(User.avatar_url(user))],
|
||||
[]},
|
||||
{:meta, [property: "og:image:width", content: 150], []},
|
||||
{:meta, [property: "og:image:height", content: 150], []}
|
||||
|
|
@ -70,8 +58,9 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
|
|||
], []},
|
||||
{:meta, [property: "og:url", content: user.uri || user.ap_id], []},
|
||||
{:meta, [property: "og:description", content: truncated_bio], []},
|
||||
{:meta, [property: "og:type", content: "website"], []},
|
||||
{:meta, [property: "og:image", content: Utils.attachment_url(User.avatar_url(user))], []},
|
||||
{:meta, [property: "og:type", content: "article"], []},
|
||||
{:meta, [property: "og:image", content: MediaProxy.preview_url(User.avatar_url(user))],
|
||||
[]},
|
||||
{:meta, [property: "og:image:width", content: 150], []},
|
||||
{:meta, [property: "og:image:height", content: 150], []}
|
||||
]
|
||||
|
|
@ -82,29 +71,35 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
|
|||
Enum.reduce(attachments, [], fn attachment, acc ->
|
||||
rendered_tags =
|
||||
Enum.reduce(attachment["url"], [], fn url, acc ->
|
||||
# TODO: Add additional properties to objects when we have the data available.
|
||||
# Also, Whatsapp only wants JPEG or PNGs. It seems that if we add a second og:image
|
||||
# TODO: Whatsapp only wants JPEG or PNGs. It seems that if we add a second og:image
|
||||
# object when a Video or GIF is attached it will display that in Whatsapp Rich Preview.
|
||||
case Utils.fetch_media_type(@media_types, url["mediaType"]) do
|
||||
"audio" ->
|
||||
[
|
||||
{:meta, [property: "og:audio", content: Utils.attachment_url(url["href"])], []}
|
||||
{:meta, [property: "og:audio", content: MediaProxy.url(url["href"])], []}
|
||||
| acc
|
||||
]
|
||||
|
||||
# Not using preview_url for this. It saves bandwidth, but the image dimensions will
|
||||
# be wrong. We generate it on the fly and have no way to capture or analyze the
|
||||
# image to get the dimensions. This can be an issue for apps/FEs rendering images
|
||||
# in timelines too, but you can get clever with the aspect ratio metadata as a
|
||||
# workaround.
|
||||
"image" ->
|
||||
[
|
||||
{:meta, [property: "og:image", content: Utils.attachment_url(url["href"])], []},
|
||||
{:meta, [property: "og:image:width", content: 150], []},
|
||||
{:meta, [property: "og:image:height", content: 150], []}
|
||||
{:meta, [property: "og:image", content: MediaProxy.url(url["href"])], []},
|
||||
{:meta, [property: "og:image:alt", content: attachment["name"]], []}
|
||||
| acc
|
||||
]
|
||||
|> maybe_add_dimensions(url)
|
||||
|
||||
"video" ->
|
||||
[
|
||||
{:meta, [property: "og:video", content: Utils.attachment_url(url["href"])], []}
|
||||
{:meta, [property: "og:video", content: MediaProxy.url(url["href"])], []}
|
||||
| acc
|
||||
]
|
||||
|> maybe_add_dimensions(url)
|
||||
|> maybe_add_video_thumbnail(url)
|
||||
|
||||
_ ->
|
||||
acc
|
||||
|
|
@ -116,4 +111,38 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
|
|||
end
|
||||
|
||||
defp build_attachments(_), do: []
|
||||
|
||||
# We can use url["mediaType"] to dynamically fill the metadata
|
||||
defp maybe_add_dimensions(metadata, url) do
|
||||
type = url["mediaType"] |> String.split("/") |> List.first()
|
||||
|
||||
cond do
|
||||
!is_nil(url["height"]) && !is_nil(url["width"]) ->
|
||||
metadata ++
|
||||
[
|
||||
{:meta, [property: "og:#{type}:width", content: "#{url["width"]}"], []},
|
||||
{:meta, [property: "og:#{type}:height", content: "#{url["height"]}"], []}
|
||||
]
|
||||
|
||||
true ->
|
||||
metadata
|
||||
end
|
||||
end
|
||||
|
||||
# Media Preview Proxy makes thumbnails of videos without resizing, so we can trust the
|
||||
# width and height of the source video.
|
||||
defp maybe_add_video_thumbnail(metadata, url) do
|
||||
cond do
|
||||
Pleroma.Config.get([:media_preview_proxy, :enabled], false) ->
|
||||
metadata ++
|
||||
[
|
||||
{:meta, [property: "og:image:width", content: "#{url["width"]}"], []},
|
||||
{:meta, [property: "og:image:height", content: "#{url["height"]}"], []},
|
||||
{:meta, [property: "og:image", content: MediaProxy.preview_url(url["href"])], []}
|
||||
]
|
||||
|
||||
true ->
|
||||
metadata
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.MediaProxy
|
||||
alias Pleroma.Web.Metadata
|
||||
alias Pleroma.Web.Metadata.Providers.Provider
|
||||
alias Pleroma.Web.Metadata.Utils
|
||||
|
|
@ -16,17 +17,10 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
|
|||
def build_tags(%{activity_id: id, object: object, user: user}) do
|
||||
attachments = build_attachments(id, object)
|
||||
scrubbed_content = Utils.scrub_html_and_truncate(object)
|
||||
# Zero width space
|
||||
content =
|
||||
if scrubbed_content != "" and scrubbed_content != "\u200B" do
|
||||
"“" <> scrubbed_content <> "”"
|
||||
else
|
||||
""
|
||||
end
|
||||
|
||||
[
|
||||
title_tag(user),
|
||||
{:meta, [property: "twitter:description", content: content], []}
|
||||
{:meta, [property: "twitter:description", content: scrubbed_content], []}
|
||||
] ++
|
||||
if attachments == [] or Metadata.activity_nsfw?(object) do
|
||||
[
|
||||
|
|
@ -55,14 +49,14 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
|
|||
end
|
||||
|
||||
def image_tag(user) do
|
||||
{:meta, [property: "twitter:image", content: Utils.attachment_url(User.avatar_url(user))], []}
|
||||
{:meta, [property: "twitter:image", content: MediaProxy.preview_url(User.avatar_url(user))],
|
||||
[]}
|
||||
end
|
||||
|
||||
defp build_attachments(id, %{data: %{"attachment" => attachments}}) do
|
||||
Enum.reduce(attachments, [], fn attachment, acc ->
|
||||
rendered_tags =
|
||||
Enum.reduce(attachment["url"], [], fn url, acc ->
|
||||
# TODO: Add additional properties to objects when we have the data available.
|
||||
case Utils.fetch_media_type(@media_types, url["mediaType"]) do
|
||||
"audio" ->
|
||||
[
|
||||
|
|
@ -73,25 +67,37 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
|
|||
| acc
|
||||
]
|
||||
|
||||
# Not using preview_url for this. It saves bandwidth, but the image dimensions will
|
||||
# be wrong. We generate it on the fly and have no way to capture or analyze the
|
||||
# image to get the dimensions. This can be an issue for apps/FEs rendering images
|
||||
# in timelines too, but you can get clever with the aspect ratio metadata as a
|
||||
# workaround.
|
||||
"image" ->
|
||||
[
|
||||
{:meta, [property: "twitter:card", content: "summary_large_image"], []},
|
||||
{:meta,
|
||||
[
|
||||
property: "twitter:player",
|
||||
content: Utils.attachment_url(url["href"])
|
||||
content: MediaProxy.url(url["href"])
|
||||
], []}
|
||||
| acc
|
||||
]
|
||||
|> maybe_add_dimensions(url)
|
||||
|
||||
# TODO: Need the true width and height values here or Twitter renders an iFrame with
|
||||
# a bad aspect ratio
|
||||
"video" ->
|
||||
# fallback to old placeholder values
|
||||
height = url["height"] || 480
|
||||
width = url["width"] || 480
|
||||
|
||||
[
|
||||
{:meta, [property: "twitter:card", content: "player"], []},
|
||||
{:meta, [property: "twitter:player", content: player_url(id)], []},
|
||||
{:meta, [property: "twitter:player:width", content: "480"], []},
|
||||
{:meta, [property: "twitter:player:height", content: "480"], []}
|
||||
{:meta, [property: "twitter:player:width", content: "#{width}"], []},
|
||||
{:meta, [property: "twitter:player:height", content: "#{height}"], []},
|
||||
{:meta, [property: "twitter:player:stream", content: MediaProxy.url(url["href"])],
|
||||
[]},
|
||||
{:meta,
|
||||
[property: "twitter:player:stream:content_type", content: url["mediaType"]], []}
|
||||
| acc
|
||||
]
|
||||
|
||||
|
|
@ -109,4 +115,20 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
|
|||
defp player_url(id) do
|
||||
Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice_player, id)
|
||||
end
|
||||
|
||||
# Videos have problems without dimensions, but we used to not provide WxH for images.
|
||||
# A default (read: incorrect) fallback for images is likely to cause rendering bugs.
|
||||
defp maybe_add_dimensions(metadata, url) do
|
||||
cond do
|
||||
!is_nil(url["height"]) && !is_nil(url["width"]) ->
|
||||
metadata ++
|
||||
[
|
||||
{:meta, [property: "twitter:player:width", content: "#{url["width"]}"], []},
|
||||
{:meta, [property: "twitter:player:height", content: "#{url["height"]}"], []}
|
||||
]
|
||||
|
||||
true ->
|
||||
metadata
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ defmodule Pleroma.Web.Metadata.Utils do
|
|||
alias Pleroma.Emoji
|
||||
alias Pleroma.Formatter
|
||||
alias Pleroma.HTML
|
||||
alias Pleroma.Web.MediaProxy
|
||||
|
||||
def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do
|
||||
content
|
||||
|
|
@ -38,10 +37,6 @@ defmodule Pleroma.Web.Metadata.Utils do
|
|||
|
||||
def scrub_html(content), do: content
|
||||
|
||||
def attachment_url(url) do
|
||||
MediaProxy.preview_url(url)
|
||||
end
|
||||
|
||||
def user_name_string(user) do
|
||||
"#{user.name} " <>
|
||||
if user.local do
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
alias Pleroma.Registration
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.Auth.Authenticator
|
||||
alias Pleroma.Web.ControllerHelper
|
||||
alias Pleroma.Web.Auth.WrapperAuthenticator, as: Authenticator
|
||||
alias Pleroma.Web.OAuth.App
|
||||
alias Pleroma.Web.OAuth.Authorization
|
||||
alias Pleroma.Web.OAuth.MFAController
|
||||
|
|
@ -24,6 +23,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
alias Pleroma.Web.OAuth.Token.Strategy.RefreshToken
|
||||
alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken
|
||||
alias Pleroma.Web.Plugs.RateLimiter
|
||||
alias Pleroma.Web.Utils.Params
|
||||
|
||||
require Logger
|
||||
|
||||
|
|
@ -32,10 +32,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
plug(:fetch_session)
|
||||
plug(:fetch_flash)
|
||||
|
||||
plug(:skip_plug, [
|
||||
Pleroma.Web.Plugs.OAuthScopesPlug,
|
||||
Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
])
|
||||
plug(:skip_auth)
|
||||
|
||||
plug(RateLimiter, [name: :authentication] when action == :create_authorization)
|
||||
|
||||
|
|
@ -50,7 +47,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
end
|
||||
|
||||
def authorize(%Plug.Conn{assigns: %{token: %Token{}}} = conn, %{"force_login" => _} = params) do
|
||||
if ControllerHelper.truthy_param?(params["force_login"]) do
|
||||
if Params.truthy_param?(params["force_login"]) do
|
||||
do_authorize(conn, params)
|
||||
else
|
||||
handle_existing_authorization(conn, params)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
|||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Web.Plugs.RateLimiter
|
||||
|
||||
|
|
@ -29,10 +28,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
|||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
plug(
|
||||
:skip_plug,
|
||||
[OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :confirmation_resend
|
||||
)
|
||||
plug(:skip_auth when action == :confirmation_resend)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
|
|
|
|||
|
|
@ -22,11 +22,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do
|
|||
]
|
||||
)
|
||||
|
||||
@skip_plugs [
|
||||
Pleroma.Web.Plugs.OAuthScopesPlug,
|
||||
Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
]
|
||||
plug(:skip_plug, @skip_plugs when action in [:index, :archive, :show])
|
||||
plug(:skip_auth when action in [:index, :archive, :show])
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaEmojiPackOperation
|
||||
|
||||
|
|
|
|||
|
|
@ -61,5 +61,5 @@
|
|||
<% end %>
|
||||
|
||||
<%= if Pleroma.Config.oauth_consumer_enabled?() do %>
|
||||
<%= render @view_module, Pleroma.Web.Auth.Authenticator.oauth_consumer_template(), assigns %>
|
||||
<%= render @view_module, Pleroma.Web.Auth.WrapperAuthenticator.oauth_consumer_template(), assigns %>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -7,17 +7,12 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Web.TwitterAPI.TokenView
|
||||
|
||||
require Logger
|
||||
|
||||
plug(
|
||||
:skip_plug,
|
||||
[OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :confirm_email
|
||||
)
|
||||
|
||||
plug(:skip_auth when action == :confirm_email)
|
||||
plug(:skip_plug, OAuthScopesPlug when action in [:oauth_tokens, :revoke_token])
|
||||
|
||||
action_fallback(:errors)
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
|
|||
alias Pleroma.MFA
|
||||
alias Pleroma.Object.Fetcher
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.Auth.Authenticator
|
||||
alias Pleroma.Web.Auth.TOTPAuthenticator
|
||||
alias Pleroma.Web.Auth.WrapperAuthenticator
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
@status_types ["Article", "Event", "Note", "Video", "Page", "Question"]
|
||||
|
|
@ -88,7 +88,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
|
|||
#
|
||||
def do_follow(conn, %{"authorization" => %{"name" => _, "password" => _, "id" => id}}) do
|
||||
with {_, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
|
||||
{_, {:ok, user}, _} <- {:auth, Authenticator.get_user(conn), followee},
|
||||
{_, {:ok, user}, _} <- {:auth, WrapperAuthenticator.get_user(conn), followee},
|
||||
{_, _, _, false} <- {:mfa_required, followee, user, MFA.require?(user)},
|
||||
{:ok, _, _, _} <- CommonAPI.follow(user, followee) do
|
||||
redirect(conn, to: "/users/#{followee.id}")
|
||||
|
|
|
|||
13
lib/pleroma/web/utils/guards.ex
Normal file
13
lib/pleroma/web/utils/guards.ex
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Utils.Guards do
|
||||
@moduledoc """
|
||||
Project-wide custom guards.
|
||||
See: https://hexdocs.pm/elixir/master/patterns-and-guards.html#custom-patterns-and-guards-expressions
|
||||
"""
|
||||
|
||||
@doc "Checks for non-empty string"
|
||||
defguard not_empty_string(string) when is_binary(string) and string != ""
|
||||
end
|
||||
16
lib/pleroma/web/utils/params.ex
Normal file
16
lib/pleroma/web/utils/params.ex
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Utils.Params do
|
||||
# As in Mastodon API, per https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html
|
||||
@falsy_param_values [false, 0, "0", "f", "F", "false", "False", "FALSE", "off", "OFF"]
|
||||
|
||||
defp explicitly_falsy_param?(value), do: value in @falsy_param_values
|
||||
|
||||
# Note: `nil` and `""` are considered falsy values in Pleroma
|
||||
defp falsy_param?(value),
|
||||
do: explicitly_falsy_param?(value) or value in [nil, ""]
|
||||
|
||||
def truthy_param?(value), do: not falsy_param?(value)
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue