Merge develop
This commit is contained in:
commit
9bca70b10a
51 changed files with 1157 additions and 213 deletions
|
|
@ -28,6 +28,14 @@ defmodule Mix.Tasks.Pleroma.Config do
|
|||
|> Enum.reject(fn {k, _v} -> k in [Pleroma.Repo, :env] end)
|
||||
|> Enum.each(fn {k, v} ->
|
||||
key = to_string(k) |> String.replace("Elixir.", "")
|
||||
|
||||
key =
|
||||
if String.starts_with?(key, "Pleroma.") do
|
||||
key
|
||||
else
|
||||
":" <> key
|
||||
end
|
||||
|
||||
{:ok, _} = Config.update_or_create(%{group: "pleroma", key: key, value: v})
|
||||
Mix.shell().info("#{key} is migrated.")
|
||||
end)
|
||||
|
|
@ -53,17 +61,9 @@ defmodule Mix.Tasks.Pleroma.Config do
|
|||
|
||||
Repo.all(Config)
|
||||
|> Enum.each(fn config ->
|
||||
mark =
|
||||
if String.starts_with?(config.key, "Pleroma.") or
|
||||
String.starts_with?(config.key, "Ueberauth"),
|
||||
do: ",",
|
||||
else: ":"
|
||||
|
||||
IO.write(
|
||||
file,
|
||||
"config :#{config.group}, #{config.key}#{mark} #{
|
||||
inspect(Config.from_binary(config.value))
|
||||
}\r\n"
|
||||
"config :#{config.group}, #{config.key}, #{inspect(Config.from_binary(config.value))}\r\n\r\n"
|
||||
)
|
||||
|
||||
if delete? do
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ defmodule Pleroma.Config.TransferTask do
|
|||
if String.starts_with?(setting.key, "Pleroma.") do
|
||||
"Elixir." <> setting.key
|
||||
else
|
||||
setting.key
|
||||
String.trim_leading(setting.key, ":")
|
||||
end
|
||||
|
||||
group = String.to_existing_atom(setting.group)
|
||||
|
|
|
|||
|
|
@ -35,10 +35,12 @@ defmodule Pleroma.Keys do
|
|||
end
|
||||
|
||||
def keys_from_pem(pem) do
|
||||
[private_key_code] = :public_key.pem_decode(pem)
|
||||
private_key = :public_key.pem_entry_decode(private_key_code)
|
||||
{:RSAPrivateKey, _, modulus, exponent, _, _, _, _, _, _, _} = private_key
|
||||
public_key = {:RSAPublicKey, modulus, exponent}
|
||||
{:ok, private_key, public_key}
|
||||
with [private_key_code] <- :public_key.pem_decode(pem),
|
||||
private_key <- :public_key.pem_entry_decode(private_key_code),
|
||||
{:RSAPrivateKey, _, modulus, exponent, _, _, _, _, _, _, _} <- private_key do
|
||||
{:ok, private_key, {:RSAPublicKey, modulus, exponent}}
|
||||
else
|
||||
error -> {:error, error}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ defmodule Pleroma.List do
|
|||
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||
field(:title, :string)
|
||||
field(:following, {:array, :string}, default: [])
|
||||
field(:ap_id, :string)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
|
@ -55,6 +56,10 @@ defmodule Pleroma.List do
|
|||
Repo.one(query)
|
||||
end
|
||||
|
||||
def get_by_ap_id(ap_id) do
|
||||
Repo.get_by(__MODULE__, ap_id: ap_id)
|
||||
end
|
||||
|
||||
def get_following(%Pleroma.List{following: following} = _list) do
|
||||
q =
|
||||
from(
|
||||
|
|
@ -105,7 +110,14 @@ defmodule Pleroma.List do
|
|||
|
||||
def create(title, %User{} = creator) do
|
||||
list = %Pleroma.List{user_id: creator.id, title: title}
|
||||
Repo.insert(list)
|
||||
|
||||
Repo.transaction(fn ->
|
||||
list = Repo.insert!(list)
|
||||
|
||||
list
|
||||
|> change(ap_id: "#{creator.ap_id}/lists/#{list.id}")
|
||||
|> Repo.update!()
|
||||
end)
|
||||
end
|
||||
|
||||
def follow(%Pleroma.List{following: following} = list, %User{} = followed) do
|
||||
|
|
@ -125,4 +137,19 @@ defmodule Pleroma.List do
|
|||
|> follow_changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def memberships(%User{follower_address: follower_address}) do
|
||||
Pleroma.List
|
||||
|> where([l], ^follower_address in l.following)
|
||||
|> select([l], l.ap_id)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
def memberships(_), do: []
|
||||
|
||||
def member?(%Pleroma.List{following: following}, %User{follower_address: follower_address}) do
|
||||
Enum.member?(following, follower_address)
|
||||
end
|
||||
|
||||
def member?(_, _), do: false
|
||||
end
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ defmodule Pleroma.Notification do
|
|||
|> join(:left, [n, a], tm in Pleroma.ThreadMute,
|
||||
on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data)
|
||||
)
|
||||
|> where([n, a, o, tm], is_nil(tm.id))
|
||||
|> where([n, a, o, tm], is_nil(tm.user_id))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,9 @@ defmodule Pleroma.Object.Containment do
|
|||
end
|
||||
end
|
||||
|
||||
def contain_origin(id, %{"attributedTo" => actor} = params),
|
||||
do: contain_origin(id, Map.put(params, "actor", actor))
|
||||
|
||||
def contain_origin_from_id(_id, %{"id" => nil}), do: :error
|
||||
|
||||
def contain_origin_from_id(id, %{"id" => other_id} = _params) do
|
||||
|
|
@ -60,4 +63,9 @@ defmodule Pleroma.Object.Containment do
|
|||
:error
|
||||
end
|
||||
end
|
||||
|
||||
def contain_child(%{"object" => %{"id" => id, "attributedTo" => _} = object}),
|
||||
do: contain_origin(id, object)
|
||||
|
||||
def contain_child(_), do: :ok
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,11 +6,26 @@ defmodule Pleroma.Plugs.AuthenticationPlug do
|
|||
alias Comeonin.Pbkdf2
|
||||
import Plug.Conn
|
||||
alias Pleroma.User
|
||||
require Logger
|
||||
|
||||
def init(options) do
|
||||
options
|
||||
end
|
||||
|
||||
def checkpw(password, password_hash) do
|
||||
cond do
|
||||
String.starts_with?(password_hash, "$pbkdf2") ->
|
||||
Pbkdf2.checkpw(password, password_hash)
|
||||
|
||||
String.starts_with?(password_hash, "$6") ->
|
||||
:crypt.crypt(password, password_hash) == password_hash
|
||||
|
||||
true ->
|
||||
Logger.error("Password hash not recognized")
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
|
||||
|
||||
def call(
|
||||
|
|
|
|||
|
|
@ -1191,10 +1191,12 @@ defmodule Pleroma.User do
|
|||
end
|
||||
|
||||
# OStatus Magic Key
|
||||
def public_key_from_info(%{magic_key: magic_key}) do
|
||||
def public_key_from_info(%{magic_key: magic_key}) when not is_nil(magic_key) do
|
||||
{:ok, Pleroma.Web.Salmon.decode_key(magic_key)}
|
||||
end
|
||||
|
||||
def public_key_from_info(_), do: {:error, "not found key"}
|
||||
|
||||
def get_public_key_for_ap_id(ap_id) do
|
||||
with {:ok, %User{} = user} <- get_or_fetch_by_ap_id(ap_id),
|
||||
{:ok, public_key} <- public_key_from_info(user.info) do
|
||||
|
|
@ -1454,23 +1456,16 @@ defmodule Pleroma.User do
|
|||
}
|
||||
end
|
||||
|
||||
def ensure_keys_present(user) do
|
||||
info = user.info
|
||||
|
||||
def ensure_keys_present(%User{info: info} = user) do
|
||||
if info.keys do
|
||||
{:ok, user}
|
||||
else
|
||||
{:ok, pem} = Keys.generate_rsa_pem()
|
||||
|
||||
info_cng =
|
||||
info
|
||||
|> User.Info.set_keys(pem)
|
||||
|
||||
cng =
|
||||
Ecto.Changeset.change(user)
|
||||
|> Ecto.Changeset.put_embed(:info, info_cng)
|
||||
|
||||
update_and_set_cache(cng)
|
||||
user
|
||||
|> Ecto.Changeset.change()
|
||||
|> Ecto.Changeset.put_embed(:info, User.Info.set_keys(info, pem))
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
alias Pleroma.Conversation
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Object.Containment
|
||||
alias Pleroma.Object.Fetcher
|
||||
alias Pleroma.Pagination
|
||||
alias Pleroma.Repo
|
||||
|
|
@ -26,19 +27,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
# For Announce activities, we filter the recipients based on following status for any actors
|
||||
# that match actual users. See issue #164 for more information about why this is necessary.
|
||||
defp get_recipients(%{"type" => "Announce"} = data) do
|
||||
to = data["to"] || []
|
||||
cc = data["cc"] || []
|
||||
to = Map.get(data, "to", [])
|
||||
cc = Map.get(data, "cc", [])
|
||||
bcc = Map.get(data, "bcc", [])
|
||||
actor = User.get_cached_by_ap_id(data["actor"])
|
||||
|
||||
recipients =
|
||||
(to ++ cc)
|
||||
|> Enum.filter(fn recipient ->
|
||||
Enum.filter(Enum.concat([to, cc, bcc]), fn recipient ->
|
||||
case User.get_cached_by_ap_id(recipient) do
|
||||
nil ->
|
||||
true
|
||||
|
||||
user ->
|
||||
User.following?(user, actor)
|
||||
nil -> true
|
||||
user -> User.following?(user, actor)
|
||||
end
|
||||
end)
|
||||
|
||||
|
|
@ -46,17 +44,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
end
|
||||
|
||||
defp get_recipients(%{"type" => "Create"} = data) do
|
||||
to = data["to"] || []
|
||||
cc = data["cc"] || []
|
||||
actor = data["actor"] || []
|
||||
recipients = (to ++ cc ++ [actor]) |> Enum.uniq()
|
||||
to = Map.get(data, "to", [])
|
||||
cc = Map.get(data, "cc", [])
|
||||
bcc = Map.get(data, "bcc", [])
|
||||
actor = Map.get(data, "actor", [])
|
||||
recipients = [to, cc, bcc, [actor]] |> Enum.concat() |> Enum.uniq()
|
||||
{recipients, to, cc}
|
||||
end
|
||||
|
||||
defp get_recipients(data) do
|
||||
to = data["to"] || []
|
||||
cc = data["cc"] || []
|
||||
recipients = to ++ cc
|
||||
to = Map.get(data, "to", [])
|
||||
cc = Map.get(data, "cc", [])
|
||||
bcc = Map.get(data, "bcc", [])
|
||||
recipients = Enum.concat([to, cc, bcc])
|
||||
{recipients, to, cc}
|
||||
end
|
||||
|
||||
|
|
@ -126,6 +126,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
{:ok, map} <- MRF.filter(map),
|
||||
{recipients, _, _} = get_recipients(map),
|
||||
{:fake, false, map, recipients} <- {:fake, fake, map, recipients},
|
||||
:ok <- Containment.contain_child(map),
|
||||
{:ok, map, object} <- insert_full_object(map) do
|
||||
{:ok, activity} =
|
||||
Repo.insert(%Activity{
|
||||
|
|
@ -896,13 +897,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
defp maybe_order(query, _), do: query
|
||||
|
||||
def fetch_activities_query(recipients, opts \\ %{}) do
|
||||
base_query = from(activity in Activity)
|
||||
|
||||
config = %{
|
||||
skip_thread_containment: Config.get([:instance, :skip_thread_containment])
|
||||
}
|
||||
|
||||
base_query
|
||||
Activity
|
||||
|> maybe_preload_objects(opts)
|
||||
|> maybe_preload_bookmarks(opts)
|
||||
|> maybe_set_thread_muted_field(opts)
|
||||
|
|
@ -931,11 +930,31 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
end
|
||||
|
||||
def fetch_activities(recipients, opts \\ %{}) do
|
||||
fetch_activities_query(recipients, opts)
|
||||
list_memberships = Pleroma.List.memberships(opts["user"])
|
||||
|
||||
fetch_activities_query(recipients ++ list_memberships, opts)
|
||||
|> Pagination.fetch_paginated(opts)
|
||||
|> Enum.reverse()
|
||||
|> maybe_update_cc(list_memberships, opts["user"])
|
||||
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
|
||||
%{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
|
||||
if Enum.any?(bcc, &(&1 in list_memberships)) do
|
||||
update_in(activity.data["cc"], &[user_ap_id | &1])
|
||||
else
|
||||
activity
|
||||
end
|
||||
|
||||
activity ->
|
||||
activity
|
||||
end)
|
||||
end
|
||||
|
||||
defp maybe_update_cc(activities, _, _), do: activities
|
||||
|
||||
def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
|
||||
from(activity in query,
|
||||
where:
|
||||
|
|
|
|||
|
|
@ -92,18 +92,68 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Publishes an activity to all relevant peers.
|
||||
"""
|
||||
def publish(%User{} = actor, %Activity{} = activity) do
|
||||
remote_followers =
|
||||
defp recipients(actor, activity) do
|
||||
followers =
|
||||
if actor.follower_address in activity.recipients do
|
||||
{:ok, followers} = User.get_followers(actor)
|
||||
followers |> Enum.filter(&(!&1.local))
|
||||
Enum.filter(followers, &(!&1.local))
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
Pleroma.Web.Salmon.remote_users(actor, activity) ++ followers
|
||||
end
|
||||
|
||||
defp get_cc_ap_ids(ap_id, recipients) do
|
||||
host = Map.get(URI.parse(ap_id), :host)
|
||||
|
||||
recipients
|
||||
|> Enum.filter(fn %User{ap_id: ap_id} -> Map.get(URI.parse(ap_id), :host) == host end)
|
||||
|> Enum.map(& &1.ap_id)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Publishes an activity with BCC to all relevant peers.
|
||||
"""
|
||||
|
||||
def publish(actor, %{data: %{"bcc" => bcc}} = activity) when is_list(bcc) and bcc != [] do
|
||||
public = is_public?(activity)
|
||||
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
|
||||
recipients = recipients(actor, activity)
|
||||
|
||||
recipients
|
||||
|> Enum.filter(&User.ap_enabled?/1)
|
||||
|> Enum.map(fn %{info: %{source_data: data}} -> data["inbox"] end)
|
||||
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|
||||
|> Instances.filter_reachable()
|
||||
|> Enum.each(fn {inbox, unreachable_since} ->
|
||||
%User{ap_id: ap_id} =
|
||||
Enum.find(recipients, fn %{info: %{source_data: data}} -> data["inbox"] == inbox end)
|
||||
|
||||
# Get all the recipients on the same host and add them to cc. Otherwise, a remote
|
||||
# instance would only accept a first message for the first recipient and ignore the rest.
|
||||
cc = get_cc_ap_ids(ap_id, recipients)
|
||||
|
||||
json =
|
||||
data
|
||||
|> Map.put("cc", cc)
|
||||
|> Jason.encode!()
|
||||
|
||||
Pleroma.Web.Federator.Publisher.enqueue_one(__MODULE__, %{
|
||||
inbox: inbox,
|
||||
json: json,
|
||||
actor: actor,
|
||||
id: activity.data["id"],
|
||||
unreachable_since: unreachable_since
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Publishes an activity to all relevant peers.
|
||||
"""
|
||||
def publish(%User{} = actor, %Activity{} = activity) do
|
||||
public = is_public?(activity)
|
||||
|
||||
if public && Config.get([:instance, :allow_relay]) do
|
||||
|
|
@ -114,7 +164,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
json = Jason.encode!(data)
|
||||
|
||||
(Pleroma.Web.Salmon.remote_users(activity) ++ remote_followers)
|
||||
recipients(actor, activity)
|
||||
|> Enum.filter(fn user -> User.ap_enabled?(user) end)
|
||||
|> Enum.map(fn %{info: %{source_data: data}} ->
|
||||
(is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"]
|
||||
|
|
|
|||
|
|
@ -814,13 +814,16 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
|
||||
def prepare_outgoing(%{"type" => "Create", "object" => object_id} = data) do
|
||||
object =
|
||||
Object.normalize(object_id).data
|
||||
object_id
|
||||
|> Object.normalize()
|
||||
|> Map.get(:data)
|
||||
|> prepare_object
|
||||
|
||||
data =
|
||||
data
|
||||
|> Map.put("object", object)
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
|> Map.delete("bcc")
|
||||
|
||||
{:ok, data}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -25,12 +25,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
|
||||
# Some implementations send the actor URI as the actor field, others send the entire actor object,
|
||||
# so figure out what the actor's URI is based on what we have.
|
||||
def get_ap_id(object) do
|
||||
case object do
|
||||
%{"id" => id} -> id
|
||||
id -> id
|
||||
end
|
||||
end
|
||||
def get_ap_id(%{"id" => id} = _), do: id
|
||||
def get_ap_id(id), do: id
|
||||
|
||||
def normalize_params(params) do
|
||||
Map.put(params, "actor", get_ap_id(params["actor"]))
|
||||
|
|
|
|||
|
|
@ -34,6 +34,20 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|
|||
!is_public?(activity) && !is_private?(activity)
|
||||
end
|
||||
|
||||
def is_list?(%{data: %{"listMessage" => _}}), do: true
|
||||
def is_list?(_), do: false
|
||||
|
||||
def visible_for_user?(%{actor: ap_id}, %User{ap_id: ap_id}), do: true
|
||||
|
||||
def visible_for_user?(%{data: %{"listMessage" => list_ap_id}} = activity, %User{} = user) do
|
||||
user.ap_id in activity.data["to"] ||
|
||||
list_ap_id
|
||||
|> Pleroma.List.get_by_ap_id()
|
||||
|> Pleroma.List.member?(user)
|
||||
end
|
||||
|
||||
def visible_for_user?(%{data: %{"listMessage" => _}}, nil), do: false
|
||||
|
||||
def visible_for_user?(activity, nil) do
|
||||
is_public?(activity)
|
||||
end
|
||||
|
|
@ -73,6 +87,9 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|
|||
object.data["directMessage"] == true ->
|
||||
"direct"
|
||||
|
||||
is_binary(object.data["listMessage"]) ->
|
||||
"list"
|
||||
|
||||
length(cc) > 0 ->
|
||||
"private"
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Auth.PleromaAuthenticator do
|
||||
alias Comeonin.Pbkdf2
|
||||
alias Pleroma.Plugs.AuthenticationPlug
|
||||
alias Pleroma.Registration
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
|
|
@ -16,7 +16,7 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
|
|||
def get_user(%Plug.Conn{} = conn) do
|
||||
with {:ok, {name, password}} <- fetch_credentials(conn),
|
||||
{_, %User{} = user} <- {:user, fetch_user(name)},
|
||||
{_, true} <- {:checkpw, Pbkdf2.checkpw(password, user.password_hash)} do
|
||||
{_, true} <- {:checkpw, AuthenticationPlug.checkpw(password, user.password_hash)} do
|
||||
{:ok, user}
|
||||
else
|
||||
error ->
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
defmodule Pleroma.Web.CommonAPI do
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Bookmark
|
||||
alias Pleroma.Formatter
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.ThreadMute
|
||||
|
|
@ -31,7 +30,8 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
|
||||
def unfollow(follower, unfollowed) do
|
||||
with {:ok, follower, _follow_activity} <- User.unfollow(follower, unfollowed),
|
||||
{:ok, _activity} <- ActivityPub.unfollow(follower, unfollowed) do
|
||||
{:ok, _activity} <- ActivityPub.unfollow(follower, unfollowed),
|
||||
{:ok, _unfollowed} <- User.unsubscribe(follower, unfollowed) do
|
||||
{:ok, follower}
|
||||
end
|
||||
end
|
||||
|
|
@ -175,6 +175,11 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
when visibility in ~w{public unlisted private direct},
|
||||
do: {visibility, get_replied_to_visibility(in_reply_to)}
|
||||
|
||||
def get_visibility(%{"visibility" => "list:" <> list_id}, in_reply_to) do
|
||||
visibility = {:list, String.to_integer(list_id)}
|
||||
{visibility, get_replied_to_visibility(in_reply_to)}
|
||||
end
|
||||
|
||||
def get_visibility(_, in_reply_to) when not is_nil(in_reply_to) do
|
||||
visibility = get_replied_to_visibility(in_reply_to)
|
||||
{visibility, visibility}
|
||||
|
|
@ -235,19 +240,18 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
"emoji",
|
||||
Map.merge(Formatter.get_emoji_map(full_payload), poll_emoji)
|
||||
) do
|
||||
res =
|
||||
ActivityPub.create(
|
||||
%{
|
||||
to: to,
|
||||
actor: user,
|
||||
context: context,
|
||||
object: object,
|
||||
additional: %{"cc" => cc, "directMessage" => visibility == "direct"}
|
||||
},
|
||||
Pleroma.Web.ControllerHelper.truthy_param?(data["preview"]) || false
|
||||
)
|
||||
preview? = Pleroma.Web.ControllerHelper.truthy_param?(data["preview"]) || false
|
||||
direct? = visibility == "direct"
|
||||
|
||||
res
|
||||
%{
|
||||
to: to,
|
||||
actor: user,
|
||||
context: context,
|
||||
object: object,
|
||||
additional: %{"cc" => cc, "directMessage" => direct?}
|
||||
}
|
||||
|> maybe_add_list_data(user, visibility)
|
||||
|> ActivityPub.create(preview?)
|
||||
else
|
||||
{:private_to_public, true} ->
|
||||
{:error, dgettext("errors", "The message visibility must be direct")}
|
||||
|
|
@ -351,15 +355,6 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
end
|
||||
end
|
||||
|
||||
def bookmarked?(user, activity) do
|
||||
with %Bookmark{} <- Bookmark.get(user.id, activity.id) do
|
||||
true
|
||||
else
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def report(user, data) do
|
||||
with {:account_id, %{"account_id" => account_id}} <- {:account_id, data},
|
||||
{:account, %User{} = account} <- {:account, User.get_cached_by_id(account_id)},
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
import Pleroma.Web.Gettext
|
||||
|
||||
alias Calendar.Strftime
|
||||
alias Comeonin.Pbkdf2
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Formatter
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Plugs.AuthenticationPlug
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
|
|
@ -100,12 +100,29 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
end
|
||||
end
|
||||
|
||||
def get_to_and_cc(_user, mentions, _inReplyTo, {:list, _}), do: {mentions, []}
|
||||
|
||||
def get_addressed_users(_, to) when is_list(to) do
|
||||
User.get_ap_ids_by_nicknames(to)
|
||||
end
|
||||
|
||||
def get_addressed_users(mentioned_users, _), do: mentioned_users
|
||||
|
||||
def maybe_add_list_data(activity_params, user, {:list, list_id}) do
|
||||
case Pleroma.List.get(list_id, user) do
|
||||
%Pleroma.List{} = list ->
|
||||
activity_params
|
||||
|> put_in([:additional, "bcc"], [list.ap_id])
|
||||
|> put_in([:additional, "listMessage"], list.ap_id)
|
||||
|> put_in([:object, "listMessage"], list.ap_id)
|
||||
|
||||
_ ->
|
||||
activity_params
|
||||
end
|
||||
end
|
||||
|
||||
def maybe_add_list_data(activity_params, _, _), do: activity_params
|
||||
|
||||
def make_poll_data(%{"poll" => %{"options" => options, "expires_in" => expires_in}} = data)
|
||||
when is_list(options) do
|
||||
%{max_expiration: max_expiration, min_expiration: min_expiration} =
|
||||
|
|
@ -371,7 +388,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
|
||||
def confirm_current_password(user, password) do
|
||||
with %User{local: true} = db_user <- User.get_cached_by_id(user.id),
|
||||
true <- Pbkdf2.checkpw(password, db_user.password_hash) do
|
||||
true <- AuthenticationPlug.checkpw(password, db_user.password_hash) do
|
||||
{:ok, db_user}
|
||||
else
|
||||
_ -> {:error, dgettext("errors", "Invalid password.")}
|
||||
|
|
|
|||
|
|
@ -693,11 +693,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||
else
|
||||
{:error, reason} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{"error" => reason})
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -738,11 +733,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("status.json", %{activity: activity, for: user, as: :activity})
|
||||
else
|
||||
{:error, reason} ->
|
||||
conn
|
||||
|> put_resp_content_type("application/json")
|
||||
|> send_resp(:bad_request, Jason.encode!(%{"error" => reason}))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -881,7 +871,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
|
||||
def favourited_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Activity{data: %{"object" => object}} <- Repo.get(Activity, id),
|
||||
with %Activity{data: %{"object" => object}} <- Activity.get_by_id(id),
|
||||
%Object{data: %{"likes" => likes}} <- Object.normalize(object) do
|
||||
q = from(u in User, where: u.ap_id in ^likes)
|
||||
users = Repo.all(q)
|
||||
|
|
@ -895,7 +885,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
|
||||
def reblogged_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Activity{data: %{"object" => object}} <- Repo.get(Activity, id),
|
||||
with %Activity{data: %{"object" => object}} <- Activity.get_by_id(id),
|
||||
%Object{data: %{"announcements" => announces}} <- Object.normalize(object) do
|
||||
q = from(u in User, where: u.ap_id in ^announces)
|
||||
users = Repo.all(q)
|
||||
|
|
@ -1651,6 +1641,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
render_error(conn, :not_found, "Record not found")
|
||||
end
|
||||
|
||||
def errors(conn, {:error, error_message}) do
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: error_message})
|
||||
end
|
||||
|
||||
def errors(conn, _) do
|
||||
conn
|
||||
|> put_status(:internal_server_error)
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
following: User.following?(user, target),
|
||||
followed_by: User.following?(target, user),
|
||||
blocking: User.blocks?(user, target),
|
||||
blocked_by: User.blocks?(target, user),
|
||||
muting: User.mutes?(user, target),
|
||||
muting_notifications: User.muted_notifications?(user, target),
|
||||
subscribing: User.subscribed_to?(user, target),
|
||||
|
|
@ -136,6 +137,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
|> maybe_put_notification_settings(user, opts[:for])
|
||||
|> maybe_put_settings_store(user, opts[:for], opts)
|
||||
|> maybe_put_chat_token(user, opts[:for], opts)
|
||||
|> maybe_put_activation_status(user, opts[:for])
|
||||
end
|
||||
|
||||
defp username_from_nickname(string) when is_binary(string) do
|
||||
|
|
@ -196,6 +198,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
|
||||
defp maybe_put_notification_settings(data, _, _), do: data
|
||||
|
||||
defp maybe_put_activation_status(data, user, %User{info: %{is_admin: true}}) do
|
||||
Kernel.put_in(data, [:pleroma, :deactivated], user.info.deactivated)
|
||||
end
|
||||
|
||||
defp maybe_put_activation_status(data, _, _), do: data
|
||||
|
||||
defp image_url(%{"url" => [%{"href" => href} | _]}), do: href
|
||||
defp image_url(_), do: nil
|
||||
end
|
||||
|
|
|
|||
|
|
@ -382,7 +382,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
%{
|
||||
# Mastodon uses separate ids for polls, but an object can't have
|
||||
# more than one poll embedded so object id is fine
|
||||
id: object.id,
|
||||
id: to_string(object.id),
|
||||
expires_at: Utils.to_masto_date(end_time),
|
||||
expired: expired,
|
||||
multiple: multiple,
|
||||
|
|
|
|||
|
|
@ -3,68 +3,71 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MediaProxy do
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Web
|
||||
|
||||
@base64_opts [padding: false]
|
||||
|
||||
def url(nil), do: nil
|
||||
|
||||
def url(""), do: nil
|
||||
|
||||
def url(url) when is_nil(url) or url == "", do: nil
|
||||
def url("/" <> _ = url), do: url
|
||||
|
||||
def url(url) do
|
||||
if !enabled?() or local?(url) or whitelisted?(url) do
|
||||
if disabled?() or local?(url) or whitelisted?(url) do
|
||||
url
|
||||
else
|
||||
encode_url(url)
|
||||
end
|
||||
end
|
||||
|
||||
defp enabled?, do: Pleroma.Config.get([:media_proxy, :enabled], false)
|
||||
defp disabled?, do: !Config.get([:media_proxy, :enabled], false)
|
||||
|
||||
defp local?(url), do: String.starts_with?(url, Pleroma.Web.base_url())
|
||||
|
||||
defp whitelisted?(url) do
|
||||
%{host: domain} = URI.parse(url)
|
||||
|
||||
Enum.any?(Pleroma.Config.get([:media_proxy, :whitelist]), fn pattern ->
|
||||
Enum.any?(Config.get([:media_proxy, :whitelist]), fn pattern ->
|
||||
String.equivalent?(domain, pattern)
|
||||
end)
|
||||
end
|
||||
|
||||
def encode_url(url) do
|
||||
secret = Pleroma.Config.get([Pleroma.Web.Endpoint, :secret_key_base])
|
||||
base64 = Base.url_encode64(url, @base64_opts)
|
||||
sig = :crypto.hmac(:sha, secret, base64)
|
||||
sig64 = sig |> Base.url_encode64(@base64_opts)
|
||||
|
||||
sig64 =
|
||||
base64
|
||||
|> signed_url
|
||||
|> Base.url_encode64(@base64_opts)
|
||||
|
||||
build_url(sig64, base64, filename(url))
|
||||
end
|
||||
|
||||
def decode_url(sig, url) do
|
||||
secret = Pleroma.Config.get([Pleroma.Web.Endpoint, :secret_key_base])
|
||||
sig = Base.url_decode64!(sig, @base64_opts)
|
||||
local_sig = :crypto.hmac(:sha, secret, url)
|
||||
|
||||
if local_sig == sig do
|
||||
with {:ok, sig} <- Base.url_decode64(sig, @base64_opts),
|
||||
signature when signature == sig <- signed_url(url) do
|
||||
{:ok, Base.url_decode64!(url, @base64_opts)}
|
||||
else
|
||||
{:error, :invalid_signature}
|
||||
_ -> {:error, :invalid_signature}
|
||||
end
|
||||
end
|
||||
|
||||
defp signed_url(url) do
|
||||
:crypto.hmac(:sha, Config.get([Web.Endpoint, :secret_key_base]), url)
|
||||
end
|
||||
|
||||
def filename(url_or_path) do
|
||||
if path = URI.parse(url_or_path).path, do: Path.basename(path)
|
||||
end
|
||||
|
||||
def build_url(sig_base64, url_base64, filename \\ nil) do
|
||||
[
|
||||
Pleroma.Config.get([:media_proxy, :base_url], Pleroma.Web.base_url()),
|
||||
Pleroma.Config.get([:media_proxy, :base_url], Web.base_url()),
|
||||
"proxy",
|
||||
sig_base64,
|
||||
url_base64,
|
||||
filename
|
||||
]
|
||||
|> Enum.filter(fn value -> value end)
|
||||
|> Enum.filter(& &1)
|
||||
|> Path.join()
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
|
|||
with config <- Pleroma.Config.get([:media_proxy], []),
|
||||
true <- Keyword.get(config, :enabled, false),
|
||||
{:ok, url} <- MediaProxy.decode_url(sig64, url64),
|
||||
:ok <- filename_matches(Map.has_key?(params, "filename"), conn.request_path, url) do
|
||||
:ok <- filename_matches(params, conn.request_path, url) do
|
||||
ReverseProxy.call(conn, url, Keyword.get(config, :proxy_opts, @default_proxy_opts))
|
||||
else
|
||||
false ->
|
||||
|
|
@ -27,13 +27,20 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
|
|||
end
|
||||
end
|
||||
|
||||
def filename_matches(has_filename, path, url) do
|
||||
filename = url |> MediaProxy.filename()
|
||||
def filename_matches(%{"filename" => _} = _, path, url) do
|
||||
filename = MediaProxy.filename(url)
|
||||
|
||||
if has_filename && filename && Path.basename(path) != filename do
|
||||
if filename && does_not_match(path, filename) do
|
||||
{:wrong_filename, filename}
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
def filename_matches(_, _, _), do: :ok
|
||||
|
||||
defp does_not_match(path, filename) do
|
||||
basename = Path.basename(path)
|
||||
basename != filename and URI.decode(basename) != filename and URI.encode(basename) != filename
|
||||
end
|
||||
end
|
||||
|
|
@ -123,11 +123,26 @@ defmodule Pleroma.Web.Salmon do
|
|||
{:ok, salmon}
|
||||
end
|
||||
|
||||
def remote_users(%{data: %{"to" => to} = data}) do
|
||||
to = to ++ (data["cc"] || [])
|
||||
def remote_users(%User{id: user_id}, %{data: %{"to" => to} = data}) do
|
||||
cc = Map.get(data, "cc", [])
|
||||
|
||||
to
|
||||
|> Enum.map(fn id -> User.get_cached_by_ap_id(id) end)
|
||||
bcc =
|
||||
data
|
||||
|> Map.get("bcc", [])
|
||||
|> Enum.reduce([], fn ap_id, bcc ->
|
||||
case Pleroma.List.get_by_ap_id(ap_id) do
|
||||
%Pleroma.List{user_id: ^user_id} = list ->
|
||||
{:ok, following} = Pleroma.List.get_following(list)
|
||||
bcc ++ Enum.map(following, & &1.ap_id)
|
||||
|
||||
_ ->
|
||||
bcc
|
||||
end
|
||||
end)
|
||||
|
||||
[to, cc, bcc]
|
||||
|> Enum.concat()
|
||||
|> Enum.map(&User.get_cached_by_ap_id/1)
|
||||
|> Enum.filter(fn user -> user && !user.local end)
|
||||
end
|
||||
|
||||
|
|
@ -191,7 +206,7 @@ defmodule Pleroma.Web.Salmon do
|
|||
{:ok, private, _} = Keys.keys_from_pem(keys)
|
||||
{:ok, feed} = encode(private, feed)
|
||||
|
||||
remote_users = remote_users(activity)
|
||||
remote_users = remote_users(user, activity)
|
||||
|
||||
salmon_urls = Enum.map(remote_users, & &1.info.salmon)
|
||||
reachable_urls_metadata = Instances.filter_reachable(salmon_urls)
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
|
||||
require Logger
|
||||
|
||||
alias Comeonin.Pbkdf2
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Emoji
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.Plugs.AuthenticationPlug
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
|
|
@ -96,7 +96,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
name = followee.nickname
|
||||
|
||||
with %User{} = user <- User.get_cached_by_nickname(username),
|
||||
true <- Pbkdf2.checkpw(password, user.password_hash),
|
||||
true <- AuthenticationPlug.checkpw(password, user.password_hash),
|
||||
%User{} = _followed <- User.get_cached_by_id(id),
|
||||
{:ok, follower} <- User.follow(user, followee),
|
||||
{:ok, _activity} <- ActivityPub.follow(follower, followee) do
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue