Merge remote-tracking branch 'pleroma/develop' into feature/addressable-lists
This commit is contained in:
commit
f333041a0a
64 changed files with 1191 additions and 212 deletions
|
|
@ -5,6 +5,7 @@
|
|||
defmodule Mix.Tasks.Pleroma.Database do
|
||||
alias Mix.Tasks.Pleroma.Common
|
||||
alias Pleroma.Conversation
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
require Logger
|
||||
|
|
@ -23,6 +24,10 @@ defmodule Mix.Tasks.Pleroma.Database do
|
|||
Options:
|
||||
- `--vacuum` - run `VACUUM FULL` after the embedded objects are replaced with their references
|
||||
|
||||
## Prune old objects from the database
|
||||
|
||||
mix pleroma.database prune_objects
|
||||
|
||||
## Create a conversation for all existing DMs. Can be safely re-run.
|
||||
|
||||
mix pleroma.database bump_all_conversations
|
||||
|
|
@ -72,4 +77,46 @@ defmodule Mix.Tasks.Pleroma.Database do
|
|||
Enum.each(users, &User.remove_duplicated_following/1)
|
||||
Enum.each(users, &User.update_follower_count/1)
|
||||
end
|
||||
|
||||
def run(["prune_objects" | args]) do
|
||||
import Ecto.Query
|
||||
|
||||
{options, [], []} =
|
||||
OptionParser.parse(
|
||||
args,
|
||||
strict: [
|
||||
vacuum: :boolean
|
||||
]
|
||||
)
|
||||
|
||||
Common.start_pleroma()
|
||||
|
||||
deadline = Pleroma.Config.get([:instance, :remote_post_retention_days])
|
||||
|
||||
Logger.info("Pruning objects older than #{deadline} days")
|
||||
|
||||
time_deadline =
|
||||
NaiveDateTime.utc_now()
|
||||
|> NaiveDateTime.add(-(deadline * 86_400))
|
||||
|
||||
public = "https://www.w3.org/ns/activitystreams#Public"
|
||||
|
||||
from(o in Object,
|
||||
where: fragment("?->'to' \\? ? OR ?->'cc' \\? ?", o.data, ^public, o.data, ^public),
|
||||
where: o.inserted_at < ^time_deadline,
|
||||
where:
|
||||
fragment("split_part(?->>'actor', '/', 3) != ?", o.data, ^Pleroma.Web.Endpoint.host())
|
||||
)
|
||||
|> Repo.delete_all(timeout: :infinity)
|
||||
|
||||
if Keyword.get(options, :vacuum) do
|
||||
Logger.info("Runnning VACUUM FULL")
|
||||
|
||||
Repo.query!(
|
||||
"vacuum full;",
|
||||
[],
|
||||
timeout: :infinity
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.Activity do
|
|||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.ThreadMute
|
||||
alias Pleroma.User
|
||||
|
||||
import Ecto.Changeset
|
||||
|
|
@ -37,6 +38,7 @@ defmodule Pleroma.Activity do
|
|||
field(:local, :boolean, default: true)
|
||||
field(:actor, :string)
|
||||
field(:recipients, {:array, :string}, default: [])
|
||||
field(:thread_muted?, :boolean, virtual: true)
|
||||
# This is a fake relation, do not use outside of with_preloaded_bookmark/get_bookmark
|
||||
has_one(:bookmark, Bookmark)
|
||||
has_many(:notifications, Notification, on_delete: :delete_all)
|
||||
|
|
@ -90,6 +92,16 @@ defmodule Pleroma.Activity do
|
|||
|
||||
def with_preloaded_bookmark(query, _), do: query
|
||||
|
||||
def with_set_thread_muted_field(query, %User{} = user) do
|
||||
from([a] in query,
|
||||
left_join: tm in ThreadMute,
|
||||
on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data),
|
||||
select: %Activity{a | thread_muted?: not is_nil(tm.id)}
|
||||
)
|
||||
end
|
||||
|
||||
def with_set_thread_muted_field(query, _), do: query
|
||||
|
||||
def get_by_ap_id(ap_id) do
|
||||
Repo.one(
|
||||
from(
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ defmodule Pleroma.Application do
|
|||
hackney_pool_children() ++
|
||||
[
|
||||
worker(Pleroma.Web.Federator.RetryQueue, []),
|
||||
worker(Pleroma.Web.OAuth.Token.CleanWorker, []),
|
||||
worker(Pleroma.Stats, []),
|
||||
worker(Task, [&Pleroma.Web.Push.init/0], restart: :temporary, id: :web_push_init),
|
||||
worker(Task, [&Pleroma.Web.Federator.init/0], restart: :temporary, id: :federator_init)
|
||||
|
|
@ -131,19 +132,22 @@ defmodule Pleroma.Application do
|
|||
defp setup_instrumenters do
|
||||
require Prometheus.Registry
|
||||
|
||||
:ok =
|
||||
:telemetry.attach(
|
||||
"prometheus-ecto",
|
||||
[:pleroma, :repo, :query],
|
||||
&Pleroma.Repo.Instrumenter.handle_event/4,
|
||||
%{}
|
||||
)
|
||||
if Application.get_env(:prometheus, Pleroma.Repo.Instrumenter) do
|
||||
:ok =
|
||||
:telemetry.attach(
|
||||
"prometheus-ecto",
|
||||
[:pleroma, :repo, :query],
|
||||
&Pleroma.Repo.Instrumenter.handle_event/4,
|
||||
%{}
|
||||
)
|
||||
|
||||
Pleroma.Repo.Instrumenter.setup()
|
||||
end
|
||||
|
||||
Prometheus.Registry.register_collector(:prometheus_process_collector)
|
||||
Pleroma.Web.Endpoint.MetricsExporter.setup()
|
||||
Pleroma.Web.Endpoint.PipelineInstrumenter.setup()
|
||||
Pleroma.Web.Endpoint.Instrumenter.setup()
|
||||
Pleroma.Repo.Instrumenter.setup()
|
||||
end
|
||||
|
||||
def enabled_hackney_pools do
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ defmodule Pleroma.Formatter do
|
|||
alias Pleroma.User
|
||||
alias Pleroma.Web.MediaProxy
|
||||
|
||||
@safe_mention_regex ~r/^(\s*(?<mentions>@.+?\s+)+)(?<rest>.*)/
|
||||
@safe_mention_regex ~r/^(\s*(?<mentions>@.+?\s+)+)(?<rest>.*)/s
|
||||
@link_regex ~r"((?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~%:/?#[\]@!\$&'\(\)\*\+,;=.]+)|[0-9a-z+\-\.]+:[0-9a-z$-_.+!*'(),]+"ui
|
||||
@markdown_characters_regex ~r/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ defmodule Pleroma.HTTP.Connection do
|
|||
"""
|
||||
|
||||
@hackney_options [
|
||||
connect_timeout: 2_000,
|
||||
connect_timeout: 10_000,
|
||||
recv_timeout: 20_000,
|
||||
follow_redirect: true,
|
||||
pool: :federation
|
||||
|
|
|
|||
|
|
@ -45,8 +45,15 @@ defmodule Pleroma.HTTP.RequestBuilder do
|
|||
Add headers to the request
|
||||
"""
|
||||
@spec headers(map(), list(tuple)) :: map()
|
||||
def headers(request, h) do
|
||||
Map.put_new(request, :headers, h)
|
||||
def headers(request, header_list) do
|
||||
header_list =
|
||||
if Pleroma.Config.get([:http, :send_user_agent]) do
|
||||
header_list ++ [{"User-Agent", Pleroma.Application.user_agent()}]
|
||||
else
|
||||
header_list
|
||||
end
|
||||
|
||||
Map.put_new(request, :headers, header_list)
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
|
|
|||
44
lib/pleroma/keys.ex
Normal file
44
lib/pleroma/keys.ex
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Keys do
|
||||
# Native generation of RSA keys is only available since OTP 20+ and in default build conditions
|
||||
# We try at compile time to generate natively an RSA key otherwise we fallback on the old way.
|
||||
try do
|
||||
_ = :public_key.generate_key({:rsa, 2048, 65_537})
|
||||
|
||||
def generate_rsa_pem do
|
||||
key = :public_key.generate_key({:rsa, 2048, 65_537})
|
||||
entry = :public_key.pem_entry_encode(:RSAPrivateKey, key)
|
||||
pem = :public_key.pem_encode([entry]) |> String.trim_trailing()
|
||||
{:ok, pem}
|
||||
end
|
||||
rescue
|
||||
_ ->
|
||||
def generate_rsa_pem do
|
||||
port = Port.open({:spawn, "openssl genrsa"}, [:binary])
|
||||
|
||||
{:ok, pem} =
|
||||
receive do
|
||||
{^port, {:data, pem}} -> {:ok, pem}
|
||||
end
|
||||
|
||||
Port.close(port)
|
||||
|
||||
if Regex.match?(~r/RSA PRIVATE KEY/, pem) do
|
||||
{:ok, pem}
|
||||
else
|
||||
:error
|
||||
end
|
||||
end
|
||||
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}
|
||||
end
|
||||
end
|
||||
|
|
@ -130,6 +130,13 @@ defmodule Pleroma.Object do
|
|||
end
|
||||
end
|
||||
|
||||
def prune(%Object{data: %{"id" => id}} = object) do
|
||||
with {:ok, object} <- Repo.delete(object),
|
||||
{:ok, true} <- Cachex.del(:object_cache, "object:#{id}") do
|
||||
{:ok, object}
|
||||
end
|
||||
end
|
||||
|
||||
def set_cache(%Object{data: %{"id" => ap_id}} = object) do
|
||||
Cachex.put(:object_cache, "object:#{ap_id}", object)
|
||||
{:ok, object}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,19 @@ defmodule Pleroma.Object.Fetcher do
|
|||
|
||||
@httpoison Application.get_env(:pleroma, :httpoison)
|
||||
|
||||
defp reinject_object(data) do
|
||||
Logger.debug("Reinjecting object #{data["id"]}")
|
||||
|
||||
with data <- Transmogrifier.fix_object(data),
|
||||
{:ok, object} <- Object.create(data) do
|
||||
{:ok, object}
|
||||
else
|
||||
e ->
|
||||
Logger.error("Error while processing object: #{inspect(e)}")
|
||||
{:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
# TODO:
|
||||
# This will create a Create activity, which we need internally at the moment.
|
||||
def fetch_object_from_id(id) do
|
||||
|
|
@ -26,12 +39,17 @@ defmodule Pleroma.Object.Fetcher do
|
|||
"object" => data
|
||||
},
|
||||
:ok <- Containment.contain_origin(id, params),
|
||||
{:ok, activity} <- Transmogrifier.handle_incoming(params) do
|
||||
{:ok, Object.normalize(activity, false)}
|
||||
{:ok, activity} <- Transmogrifier.handle_incoming(params),
|
||||
{:object, _data, %Object{} = object} <-
|
||||
{:object, data, Object.normalize(activity, false)} do
|
||||
{:ok, object}
|
||||
else
|
||||
{:error, {:reject, nil}} ->
|
||||
{:reject, nil}
|
||||
|
||||
{:object, data, nil} ->
|
||||
reinject_object(data)
|
||||
|
||||
object = %Object{} ->
|
||||
{:ok, object}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,11 +5,10 @@
|
|||
defmodule Pleroma.Signature do
|
||||
@behaviour HTTPSignatures.Adapter
|
||||
|
||||
alias Pleroma.Keys
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.Salmon
|
||||
alias Pleroma.Web.WebFinger
|
||||
|
||||
def fetch_public_key(conn) do
|
||||
with actor_id <- Utils.get_ap_id(conn.params["actor"]),
|
||||
|
|
@ -33,8 +32,8 @@ defmodule Pleroma.Signature do
|
|||
end
|
||||
|
||||
def sign(%User{} = user, headers) do
|
||||
with {:ok, %{info: %{keys: keys}}} <- WebFinger.ensure_keys_present(user),
|
||||
{:ok, private_key, _} <- Salmon.keys_from_pem(keys) do
|
||||
with {:ok, %{info: %{keys: keys}}} <- User.ensure_keys_present(user),
|
||||
{:ok, private_key, _} <- Keys.keys_from_pem(keys) do
|
||||
HTTPSignatures.sign(private_key, user.ap_id <> "#main-key", headers)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.User do
|
|||
|
||||
alias Comeonin.Pbkdf2
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Keys
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Registration
|
||||
|
|
@ -1402,4 +1403,44 @@ defmodule Pleroma.User do
|
|||
|> put_embed(:info, info_changeset)
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
def get_mascot(%{info: %{mascot: %{} = mascot}}) when not is_nil(mascot) do
|
||||
mascot
|
||||
end
|
||||
|
||||
def get_mascot(%{info: %{mascot: mascot}}) when is_nil(mascot) do
|
||||
# use instance-default
|
||||
config = Pleroma.Config.get([:assets, :mascots])
|
||||
default_mascot = Pleroma.Config.get([:assets, :default_mascot])
|
||||
mascot = Keyword.get(config, default_mascot)
|
||||
|
||||
%{
|
||||
"id" => "default-mascot",
|
||||
"url" => mascot[:url],
|
||||
"preview_url" => mascot[:url],
|
||||
"pleroma" => %{
|
||||
"mime_type" => mascot[:mime_type]
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def ensure_keys_present(user) do
|
||||
info = user.info
|
||||
|
||||
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)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ defmodule Pleroma.User.Info do
|
|||
field(:hide_favorites, :boolean, default: true)
|
||||
field(:pinned_activities, {:array, :string}, default: [])
|
||||
field(:flavour, :string, default: nil)
|
||||
field(:mascot, :map, default: nil)
|
||||
field(:emoji, {:array, :map}, default: [])
|
||||
|
||||
field(:notification_settings, :map,
|
||||
|
|
@ -248,6 +249,14 @@ defmodule Pleroma.User.Info do
|
|||
|> validate_required([:flavour])
|
||||
end
|
||||
|
||||
def mascot_update(info, url) do
|
||||
params = %{mascot: url}
|
||||
|
||||
info
|
||||
|> cast(params, [:mascot])
|
||||
|> validate_required([:mascot])
|
||||
end
|
||||
|
||||
def set_source_data(info, source_data) do
|
||||
params = %{source_data: source_data}
|
||||
|
||||
|
|
|
|||
|
|
@ -833,6 +833,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|> Activity.with_preloaded_bookmark(opts["user"])
|
||||
end
|
||||
|
||||
defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
|
||||
|
||||
defp maybe_set_thread_muted_field(query, opts) do
|
||||
query
|
||||
|> Activity.with_set_thread_muted_field(opts["user"])
|
||||
end
|
||||
|
||||
defp maybe_order(query, %{order: :desc}) do
|
||||
query
|
||||
|> order_by(desc: :id)
|
||||
|
|
@ -849,6 +856,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
Activity
|
||||
|> maybe_preload_objects(opts)
|
||||
|> maybe_preload_bookmarks(opts)
|
||||
|> maybe_set_thread_muted_field(opts)
|
||||
|> maybe_order(opts)
|
||||
|> restrict_recipients(recipients, opts["user"])
|
||||
|> restrict_tag(opts)
|
||||
|
|
@ -918,7 +926,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
end
|
||||
end
|
||||
|
||||
def user_data_from_user_object(data) do
|
||||
defp object_to_user_data(data) do
|
||||
avatar =
|
||||
data["icon"]["url"] &&
|
||||
%{
|
||||
|
|
@ -965,9 +973,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
{:ok, user_data}
|
||||
end
|
||||
|
||||
def user_data_from_user_object(data) do
|
||||
with {:ok, data} <- MRF.filter(data),
|
||||
{:ok, data} <- object_to_user_data(data) do
|
||||
{:ok, data}
|
||||
else
|
||||
e -> {:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
def fetch_and_prepare_user_from_ap_id(ap_id) do
|
||||
with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id) do
|
||||
user_data_from_user_object(data)
|
||||
with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
|
||||
{:ok, data} <- user_data_from_user_object(data) do
|
||||
{:ok, data}
|
||||
else
|
||||
e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
|
||||
end
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
|
||||
def user(conn, %{"nickname" => nickname}) do
|
||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||
{:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
|
||||
{:ok, user} <- User.ensure_keys_present(user) do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> json(UserView.render("user.json", %{user: user}))
|
||||
|
|
@ -106,7 +106,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
|
||||
def following(conn, %{"nickname" => nickname, "page" => page}) do
|
||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||
{:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
|
||||
{:ok, user} <- User.ensure_keys_present(user) do
|
||||
{page, _} = Integer.parse(page)
|
||||
|
||||
conn
|
||||
|
|
@ -117,7 +117,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
|
||||
def following(conn, %{"nickname" => nickname}) do
|
||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||
{:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
|
||||
{:ok, user} <- User.ensure_keys_present(user) do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> json(UserView.render("following.json", %{user: user}))
|
||||
|
|
@ -126,7 +126,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
|
||||
def followers(conn, %{"nickname" => nickname, "page" => page}) do
|
||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||
{:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
|
||||
{:ok, user} <- User.ensure_keys_present(user) do
|
||||
{page, _} = Integer.parse(page)
|
||||
|
||||
conn
|
||||
|
|
@ -137,7 +137,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
|
||||
def followers(conn, %{"nickname" => nickname}) do
|
||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||
{:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
|
||||
{:ok, user} <- User.ensure_keys_present(user) do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> json(UserView.render("followers.json", %{user: user}))
|
||||
|
|
@ -146,7 +146,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
|
||||
def outbox(conn, %{"nickname" => nickname} = params) do
|
||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||
{:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
|
||||
{:ok, user} <- User.ensure_keys_present(user) do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> json(UserView.render("outbox.json", %{user: user, max_id: params["max_id"]}))
|
||||
|
|
@ -195,7 +195,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
|
||||
def relay(conn, _params) do
|
||||
with %User{} = user <- Relay.get_actor(),
|
||||
{:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
|
||||
{:ok, user} <- User.ensure_keys_present(user) do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> json(UserView.render("user.json", %{user: user}))
|
||||
|
|
|
|||
|
|
@ -48,10 +48,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
|||
%{host: actor_host} = _actor_info,
|
||||
%{
|
||||
"type" => "Create",
|
||||
"object" => %{"attachment" => child_attachment} = child_object
|
||||
"object" => child_object
|
||||
} = object
|
||||
)
|
||||
when length(child_attachment) > 0 do
|
||||
) do
|
||||
object =
|
||||
if Enum.member?(Pleroma.Config.get([:mrf_simple, :media_nsfw]), actor_host) do
|
||||
tags = (child_object["tag"] || []) ++ ["nsfw"]
|
||||
|
|
@ -95,18 +94,63 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
|||
{:ok, object}
|
||||
end
|
||||
|
||||
defp check_report_removal(%{host: actor_host} = _actor_info, %{"type" => "Flag"} = object) do
|
||||
if actor_host in Pleroma.Config.get([:mrf_simple, :report_removal]) do
|
||||
{:reject, nil}
|
||||
else
|
||||
{:ok, object}
|
||||
end
|
||||
end
|
||||
|
||||
defp check_report_removal(_actor_info, object), do: {:ok, object}
|
||||
|
||||
defp check_avatar_removal(%{host: actor_host} = _actor_info, %{"icon" => _icon} = object) do
|
||||
if actor_host in Pleroma.Config.get([:mrf_simple, :avatar_removal]) do
|
||||
{:ok, Map.delete(object, "icon")}
|
||||
else
|
||||
{:ok, object}
|
||||
end
|
||||
end
|
||||
|
||||
defp check_avatar_removal(_actor_info, object), do: {:ok, object}
|
||||
|
||||
defp check_banner_removal(%{host: actor_host} = _actor_info, %{"image" => _image} = object) do
|
||||
if actor_host in Pleroma.Config.get([:mrf_simple, :banner_removal]) do
|
||||
{:ok, Map.delete(object, "image")}
|
||||
else
|
||||
{:ok, object}
|
||||
end
|
||||
end
|
||||
|
||||
defp check_banner_removal(_actor_info, object), do: {:ok, object}
|
||||
|
||||
@impl true
|
||||
def filter(object) do
|
||||
actor_info = URI.parse(object["actor"])
|
||||
def filter(%{"actor" => actor} = object) do
|
||||
actor_info = URI.parse(actor)
|
||||
|
||||
with {:ok, object} <- check_accept(actor_info, object),
|
||||
{:ok, object} <- check_reject(actor_info, object),
|
||||
{:ok, object} <- check_media_removal(actor_info, object),
|
||||
{:ok, object} <- check_media_nsfw(actor_info, object),
|
||||
{:ok, object} <- check_ftl_removal(actor_info, object) do
|
||||
{:ok, object} <- check_ftl_removal(actor_info, object),
|
||||
{:ok, object} <- check_report_removal(actor_info, object) do
|
||||
{:ok, object}
|
||||
else
|
||||
_e -> {:reject, nil}
|
||||
end
|
||||
end
|
||||
|
||||
def filter(%{"id" => actor, "type" => obj_type} = object)
|
||||
when obj_type in ["Application", "Group", "Organization", "Person", "Service"] do
|
||||
actor_info = URI.parse(actor)
|
||||
|
||||
with {:ok, object} <- check_avatar_removal(actor_info, object),
|
||||
{:ok, object} <- check_banner_removal(actor_info, object) do
|
||||
{:ok, object}
|
||||
else
|
||||
_e -> {:reject, nil}
|
||||
end
|
||||
end
|
||||
|
||||
def filter(object), do: {:ok, object}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -19,10 +19,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
|
|||
end
|
||||
|
||||
@impl true
|
||||
def filter(object) do
|
||||
actor_info = URI.parse(object["actor"])
|
||||
def filter(%{"actor" => actor} = object) do
|
||||
actor_info = URI.parse(actor)
|
||||
allow_list = Config.get([:mrf_user_allowlist, String.to_atom(actor_info.host)], [])
|
||||
|
||||
filter_by_list(object, allow_list)
|
||||
end
|
||||
|
||||
def filter(object), do: {:ok, object}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
defmodule Pleroma.Web.ActivityPub.UserView do
|
||||
use Pleroma.Web, :view
|
||||
|
||||
alias Pleroma.Keys
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
|
|
@ -12,8 +13,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.Endpoint
|
||||
alias Pleroma.Web.Router.Helpers
|
||||
alias Pleroma.Web.Salmon
|
||||
alias Pleroma.Web.WebFinger
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
|
|
@ -34,8 +33,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
|
||||
# the instance itself is not a Person, but instead an Application
|
||||
def render("user.json", %{user: %{nickname: nil} = user}) do
|
||||
{:ok, user} = WebFinger.ensure_keys_present(user)
|
||||
{:ok, _, public_key} = Salmon.keys_from_pem(user.info.keys)
|
||||
{:ok, user} = User.ensure_keys_present(user)
|
||||
{:ok, _, public_key} = Keys.keys_from_pem(user.info.keys)
|
||||
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
||||
public_key = :public_key.pem_encode([public_key])
|
||||
|
||||
|
|
@ -62,8 +61,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
end
|
||||
|
||||
def render("user.json", %{user: user}) do
|
||||
{:ok, user} = WebFinger.ensure_keys_present(user)
|
||||
{:ok, _, public_key} = Salmon.keys_from_pem(user.info.keys)
|
||||
{:ok, user} = User.ensure_keys_present(user)
|
||||
{:ok, _, public_key} = Keys.keys_from_pem(user.info.keys)
|
||||
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
||||
public_key = :public_key.pem_encode([public_key])
|
||||
|
||||
|
|
|
|||
|
|
@ -163,6 +163,7 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
bcc <- bcc_for_list(user, visibility),
|
||||
context <- make_context(in_reply_to),
|
||||
cw <- data["spoiler_text"] || "",
|
||||
sensitive <- data["sensitive"] || Enum.member?(tags, {"#nsfw", "nsfw"}),
|
||||
full_payload <- String.trim(status <> cw),
|
||||
length when length in 1..limit <- String.length(full_payload),
|
||||
object <-
|
||||
|
|
@ -175,7 +176,8 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
in_reply_to,
|
||||
tags,
|
||||
cw,
|
||||
cc
|
||||
cc,
|
||||
sensitive
|
||||
),
|
||||
object <-
|
||||
Map.put(
|
||||
|
|
|
|||
|
|
@ -232,7 +232,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
in_reply_to,
|
||||
tags,
|
||||
cw \\ nil,
|
||||
cc \\ []
|
||||
cc \\ [],
|
||||
sensitive \\ false
|
||||
) do
|
||||
object = %{
|
||||
"type" => "Note",
|
||||
|
|
@ -240,6 +241,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
"cc" => cc,
|
||||
"content" => content_html,
|
||||
"summary" => cw,
|
||||
"sensitive" => !Enum.member?(["false", "False", "0", false], sensitive),
|
||||
"context" => context,
|
||||
"attachment" => attachments,
|
||||
"actor" => actor,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ defmodule Pleroma.Web.Federator do
|
|||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.Federator.Publisher
|
||||
alias Pleroma.Web.Federator.RetryQueue
|
||||
alias Pleroma.Web.WebFinger
|
||||
alias Pleroma.Web.Websub
|
||||
|
||||
require Logger
|
||||
|
|
@ -77,9 +76,8 @@ defmodule Pleroma.Web.Federator do
|
|||
def perform(:publish, activity) do
|
||||
Logger.debug(fn -> "Running publish for #{activity.data["id"]}" end)
|
||||
|
||||
with actor when not is_nil(actor) <- User.get_cached_by_ap_id(activity.data["actor"]) do
|
||||
{:ok, actor} = WebFinger.ensure_keys_present(actor)
|
||||
|
||||
with %User{} = actor <- User.get_cached_by_ap_id(activity.data["actor"]),
|
||||
{:ok, actor} <- User.ensure_keys_present(actor) do
|
||||
Publisher.publish(actor, activity)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -707,6 +707,41 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def set_mascot(%{assigns: %{user: user}} = conn, %{"file" => file}) do
|
||||
with {:ok, object} <- ActivityPub.upload(file, actor: User.ap_id(user)),
|
||||
%{} = attachment_data <- Map.put(object.data, "id", object.id),
|
||||
%{type: type} = rendered <-
|
||||
StatusView.render("attachment.json", %{attachment: attachment_data}) do
|
||||
# Reject if not an image
|
||||
if type == "image" do
|
||||
# Sure!
|
||||
# Save to the user's info
|
||||
info_changeset = User.Info.mascot_update(user.info, rendered)
|
||||
|
||||
user_changeset =
|
||||
user
|
||||
|> Ecto.Changeset.change()
|
||||
|> Ecto.Changeset.put_embed(:info, info_changeset)
|
||||
|
||||
{:ok, _user} = User.update_and_set_cache(user_changeset)
|
||||
|
||||
conn
|
||||
|> json(rendered)
|
||||
else
|
||||
conn
|
||||
|> put_resp_content_type("application/json")
|
||||
|> send_resp(415, Jason.encode!(%{"error" => "mascots can only be images"}))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def get_mascot(%{assigns: %{user: user}} = conn, _params) do
|
||||
mascot = User.get_mascot(user)
|
||||
|
||||
conn
|
||||
|> json(mascot)
|
||||
end
|
||||
|
||||
def favourited_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with %Activity{data: %{"object" => object}} <- Repo.get(Activity, id),
|
||||
%Object{data: %{"likes" => likes}} <- Object.normalize(object) do
|
||||
|
|
@ -1009,6 +1044,30 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def status_search_query_with_gin(q, query) do
|
||||
from([a, o] in q,
|
||||
where:
|
||||
fragment(
|
||||
"to_tsvector('english', ?->>'content') @@ plainto_tsquery('english', ?)",
|
||||
o.data,
|
||||
^query
|
||||
),
|
||||
order_by: [desc: :id]
|
||||
)
|
||||
end
|
||||
|
||||
def status_search_query_with_rum(q, query) do
|
||||
from([a, o] in q,
|
||||
where:
|
||||
fragment(
|
||||
"? @@ plainto_tsquery('english', ?)",
|
||||
o.fts_content,
|
||||
^query
|
||||
),
|
||||
order_by: [fragment("? <=> now()::date", o.inserted_at)]
|
||||
)
|
||||
end
|
||||
|
||||
def status_search(user, query) do
|
||||
fetched =
|
||||
if Regex.match?(~r/https?:/, query) do
|
||||
|
|
@ -1022,20 +1081,19 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end || []
|
||||
|
||||
q =
|
||||
from(
|
||||
[a, o] in Activity.with_preloaded_object(Activity),
|
||||
from([a, o] in Activity.with_preloaded_object(Activity),
|
||||
where: fragment("?->>'type' = 'Create'", a.data),
|
||||
where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients,
|
||||
where:
|
||||
fragment(
|
||||
"to_tsvector('english', ?->>'content') @@ plainto_tsquery('english', ?)",
|
||||
o.data,
|
||||
^query
|
||||
),
|
||||
limit: 20,
|
||||
order_by: [desc: :id]
|
||||
limit: 20
|
||||
)
|
||||
|
||||
q =
|
||||
if Pleroma.Config.get([:database, :rum_enabled]) do
|
||||
status_search_query_with_rum(q, query)
|
||||
else
|
||||
status_search_query_with_gin(q, query)
|
||||
end
|
||||
|
||||
Repo.all(q) ++ fetched
|
||||
end
|
||||
|
||||
|
|
@ -1306,7 +1364,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
display_sensitive_media: false,
|
||||
reduce_motion: false,
|
||||
max_toot_chars: limit,
|
||||
mascot: "/images/pleroma-fox-tan-smol.png"
|
||||
mascot: User.get_mascot(user)["url"]
|
||||
},
|
||||
rights: %{
|
||||
delete_others_notice: present?(user.info.is_moderator),
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
fields: fields,
|
||||
bot: bot,
|
||||
source: %{
|
||||
note: "",
|
||||
note: HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")),
|
||||
sensitive: false,
|
||||
pleroma: %{}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -157,6 +157,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
|
||||
bookmarked = Activity.get_bookmark(activity, opts[:for]) != nil
|
||||
|
||||
thread_muted? =
|
||||
case activity.thread_muted? do
|
||||
thread_muted? when is_boolean(thread_muted?) -> thread_muted?
|
||||
nil -> CommonAPI.thread_muted?(user, activity)
|
||||
end
|
||||
|
||||
attachment_data = object.data["attachment"] || []
|
||||
attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment)
|
||||
|
||||
|
|
@ -228,7 +234,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
reblogged: reblogged?(activity, opts[:for]),
|
||||
favourited: present?(favorited),
|
||||
bookmarked: present?(bookmarked),
|
||||
muted: CommonAPI.thread_muted?(user, activity) || User.mutes?(opts[:for], user),
|
||||
muted: thread_muted? || User.mutes?(opts[:for], user),
|
||||
pinned: pinned?(activity, user),
|
||||
sensitive: sensitive,
|
||||
spoiler_text: summary_html,
|
||||
|
|
|
|||
41
lib/pleroma/web/mongooseim/mongoose_im_controller.ex
Normal file
41
lib/pleroma/web/mongooseim/mongoose_im_controller.ex
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MongooseIM.MongooseIMController do
|
||||
use Pleroma.Web, :controller
|
||||
alias Comeonin.Pbkdf2
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
|
||||
def user_exists(conn, %{"user" => username}) do
|
||||
with %User{} <- Repo.get_by(User, nickname: username, local: true) do
|
||||
conn
|
||||
|> json(true)
|
||||
else
|
||||
_ ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(false)
|
||||
end
|
||||
end
|
||||
|
||||
def check_password(conn, %{"user" => username, "pass" => password}) do
|
||||
with %User{password_hash: password_hash} <-
|
||||
Repo.get_by(User, nickname: username, local: true),
|
||||
true <- Pbkdf2.checkpw(password, password_hash) do
|
||||
conn
|
||||
|> json(true)
|
||||
else
|
||||
false ->
|
||||
conn
|
||||
|> put_status(403)
|
||||
|> json(false)
|
||||
|
||||
_ ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -5,7 +5,6 @@
|
|||
defmodule Pleroma.Web.OAuth.Token do
|
||||
use Ecto.Schema
|
||||
|
||||
import Ecto.Query
|
||||
import Ecto.Changeset
|
||||
|
||||
alias Pleroma.Repo
|
||||
|
|
@ -13,6 +12,7 @@ defmodule Pleroma.Web.OAuth.Token do
|
|||
alias Pleroma.Web.OAuth.App
|
||||
alias Pleroma.Web.OAuth.Authorization
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
alias Pleroma.Web.OAuth.Token.Query
|
||||
|
||||
@expires_in Pleroma.Config.get([:oauth2, :token_expires_in], 600)
|
||||
@type t :: %__MODULE__{}
|
||||
|
|
@ -31,17 +31,17 @@ defmodule Pleroma.Web.OAuth.Token do
|
|||
@doc "Gets token for app by access token"
|
||||
@spec get_by_token(App.t(), String.t()) :: {:ok, t()} | {:error, :not_found}
|
||||
def get_by_token(%App{id: app_id} = _app, token) do
|
||||
from(t in __MODULE__, where: t.app_id == ^app_id and t.token == ^token)
|
||||
Query.get_by_app(app_id)
|
||||
|> Query.get_by_token(token)
|
||||
|> Repo.find_resource()
|
||||
end
|
||||
|
||||
@doc "Gets token for app by refresh token"
|
||||
@spec get_by_refresh_token(App.t(), String.t()) :: {:ok, t()} | {:error, :not_found}
|
||||
def get_by_refresh_token(%App{id: app_id} = _app, token) do
|
||||
from(t in __MODULE__,
|
||||
where: t.app_id == ^app_id and t.refresh_token == ^token,
|
||||
preload: [:user]
|
||||
)
|
||||
Query.get_by_app(app_id)
|
||||
|> Query.get_by_refresh_token(token)
|
||||
|> Query.preload([:user])
|
||||
|> Repo.find_resource()
|
||||
end
|
||||
|
||||
|
|
@ -97,29 +97,25 @@ defmodule Pleroma.Web.OAuth.Token do
|
|||
end
|
||||
|
||||
def delete_user_tokens(%User{id: user_id}) do
|
||||
from(
|
||||
t in Token,
|
||||
where: t.user_id == ^user_id
|
||||
)
|
||||
Query.get_by_user(user_id)
|
||||
|> Repo.delete_all()
|
||||
end
|
||||
|
||||
def delete_user_token(%User{id: user_id}, token_id) do
|
||||
from(
|
||||
t in Token,
|
||||
where: t.user_id == ^user_id,
|
||||
where: t.id == ^token_id
|
||||
)
|
||||
Query.get_by_user(user_id)
|
||||
|> Query.get_by_id(token_id)
|
||||
|> Repo.delete_all()
|
||||
end
|
||||
|
||||
def delete_expired_tokens do
|
||||
Query.get_expired_tokens()
|
||||
|> Repo.delete_all()
|
||||
end
|
||||
|
||||
def get_user_tokens(%User{id: user_id}) do
|
||||
from(
|
||||
t in Token,
|
||||
where: t.user_id == ^user_id
|
||||
)
|
||||
Query.get_by_user(user_id)
|
||||
|> Query.preload([:app])
|
||||
|> Repo.all()
|
||||
|> Repo.preload(:app)
|
||||
end
|
||||
|
||||
def is_expired?(%__MODULE__{valid_until: valid_until}) do
|
||||
|
|
|
|||
41
lib/pleroma/web/oauth/token/clean_worker.ex
Normal file
41
lib/pleroma/web/oauth/token/clean_worker.ex
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.OAuth.Token.CleanWorker do
|
||||
@moduledoc """
|
||||
The module represents functions to clean an expired oauth tokens.
|
||||
"""
|
||||
|
||||
# 10 seconds
|
||||
@start_interval 10_000
|
||||
@interval Pleroma.Config.get(
|
||||
# 24 hours
|
||||
[:oauth2, :clean_expired_tokens_interval],
|
||||
86_400_000
|
||||
)
|
||||
@queue :background
|
||||
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
|
||||
def start_link, do: GenServer.start_link(__MODULE__, nil)
|
||||
|
||||
def init(_) do
|
||||
if Pleroma.Config.get([:oauth2, :clean_expired_tokens], false) do
|
||||
Process.send_after(self(), :perform, @start_interval)
|
||||
{:ok, nil}
|
||||
else
|
||||
:ignore
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def handle_info(:perform, state) do
|
||||
Process.send_after(self(), :perform, @interval)
|
||||
PleromaJobQueue.enqueue(@queue, __MODULE__, [:clean])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
# Job Worker Callbacks
|
||||
def perform(:clean), do: Token.delete_expired_tokens()
|
||||
end
|
||||
55
lib/pleroma/web/oauth/token/query.ex
Normal file
55
lib/pleroma/web/oauth/token/query.ex
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.OAuth.Token.Query do
|
||||
@moduledoc """
|
||||
Contains queries for OAuth Token.
|
||||
"""
|
||||
|
||||
import Ecto.Query, only: [from: 2]
|
||||
|
||||
@type query :: Ecto.Queryable.t() | Token.t()
|
||||
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
|
||||
@spec get_by_refresh_token(query, String.t()) :: query
|
||||
def get_by_refresh_token(query \\ Token, refresh_token) do
|
||||
from(q in query, where: q.refresh_token == ^refresh_token)
|
||||
end
|
||||
|
||||
@spec get_by_token(query, String.t()) :: query
|
||||
def get_by_token(query \\ Token, token) do
|
||||
from(q in query, where: q.token == ^token)
|
||||
end
|
||||
|
||||
@spec get_by_app(query, String.t()) :: query
|
||||
def get_by_app(query \\ Token, app_id) do
|
||||
from(q in query, where: q.app_id == ^app_id)
|
||||
end
|
||||
|
||||
@spec get_by_id(query, String.t()) :: query
|
||||
def get_by_id(query \\ Token, id) do
|
||||
from(q in query, where: q.id == ^id)
|
||||
end
|
||||
|
||||
@spec get_expired_tokens(query, DateTime.t() | nil) :: query
|
||||
def get_expired_tokens(query \\ Token, date \\ nil) do
|
||||
expired_date = date || Timex.now()
|
||||
from(q in query, where: fragment("?", q.valid_until) < ^expired_date)
|
||||
end
|
||||
|
||||
@spec get_by_user(query, String.t()) :: query
|
||||
def get_by_user(query \\ Token, user_id) do
|
||||
from(q in query, where: q.user_id == ^user_id)
|
||||
end
|
||||
|
||||
@spec preload(query, any) :: query
|
||||
def preload(query \\ Token, assoc_preload \\ [])
|
||||
|
||||
def preload(query, assoc_preload) when is_list(assoc_preload) do
|
||||
from(q in query, preload: ^assoc_preload)
|
||||
end
|
||||
|
||||
def preload(query, _assoc_preload), do: query
|
||||
end
|
||||
|
|
@ -24,6 +24,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do
|
|||
def fetch_data_for_activity(%Activity{data: %{"type" => "Create"}} = activity) do
|
||||
with true <- Pleroma.Config.get([:rich_media, :enabled]),
|
||||
%Object{} = object <- Object.normalize(activity),
|
||||
false <- object.data["sensitive"] || false,
|
||||
{:ok, page_url} <- HTML.extract_first_external_url(object, object.data["content"]),
|
||||
:ok <- validate_page_url(page_url),
|
||||
{:ok, rich_media} <- Parser.parse(page_url) do
|
||||
|
|
|
|||
|
|
@ -352,6 +352,9 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
post("/pleroma/flavour/:flavour", MastodonAPIController, :set_flavour)
|
||||
|
||||
get("/pleroma/mascot", MastodonAPIController, :get_mascot)
|
||||
put("/pleroma/mascot", MastodonAPIController, :set_mascot)
|
||||
|
||||
post("/reports", MastodonAPIController, :reports)
|
||||
end
|
||||
|
||||
|
|
@ -704,9 +707,15 @@ defmodule Pleroma.Web.Router do
|
|||
end
|
||||
end
|
||||
|
||||
scope "/", Pleroma.Web.MongooseIM do
|
||||
get("/user_exists", MongooseIMController, :user_exists)
|
||||
get("/check_password", MongooseIMController, :check_password)
|
||||
end
|
||||
|
||||
scope "/", Fallback do
|
||||
get("/registration/:token", RedirectController, :registration_page)
|
||||
get("/:maybe_nickname_or_id", RedirectController, :redirector_with_meta)
|
||||
get("/api*path", RedirectController, :api_not_implemented)
|
||||
get("/*path", RedirectController, :redirector)
|
||||
|
||||
options("/*path", RedirectController, :empty)
|
||||
|
|
@ -718,6 +727,12 @@ defmodule Fallback.RedirectController do
|
|||
alias Pleroma.User
|
||||
alias Pleroma.Web.Metadata
|
||||
|
||||
def api_not_implemented(conn, _params) do
|
||||
conn
|
||||
|> put_status(404)
|
||||
|> json(%{error: "Not implemented"})
|
||||
end
|
||||
|
||||
def redirector(conn, _params, code \\ 200) do
|
||||
conn
|
||||
|> put_resp_content_type("text/html")
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.Web.Salmon do
|
|||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Instances
|
||||
alias Pleroma.Keys
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.Federator.Publisher
|
||||
|
|
@ -89,45 +90,6 @@ defmodule Pleroma.Web.Salmon do
|
|||
"RSA.#{modulus_enc}.#{exponent_enc}"
|
||||
end
|
||||
|
||||
# Native generation of RSA keys is only available since OTP 20+ and in default build conditions
|
||||
# We try at compile time to generate natively an RSA key otherwise we fallback on the old way.
|
||||
try do
|
||||
_ = :public_key.generate_key({:rsa, 2048, 65_537})
|
||||
|
||||
def generate_rsa_pem do
|
||||
key = :public_key.generate_key({:rsa, 2048, 65_537})
|
||||
entry = :public_key.pem_entry_encode(:RSAPrivateKey, key)
|
||||
pem = :public_key.pem_encode([entry]) |> String.trim_trailing()
|
||||
{:ok, pem}
|
||||
end
|
||||
rescue
|
||||
_ ->
|
||||
def generate_rsa_pem do
|
||||
port = Port.open({:spawn, "openssl genrsa"}, [:binary])
|
||||
|
||||
{:ok, pem} =
|
||||
receive do
|
||||
{^port, {:data, pem}} -> {:ok, pem}
|
||||
end
|
||||
|
||||
Port.close(port)
|
||||
|
||||
if Regex.match?(~r/RSA PRIVATE KEY/, pem) do
|
||||
{:ok, pem}
|
||||
else
|
||||
:error
|
||||
end
|
||||
end
|
||||
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}
|
||||
end
|
||||
|
||||
def encode(private_key, doc) do
|
||||
type = "application/atom+xml"
|
||||
encoding = "base64url"
|
||||
|
|
@ -242,7 +204,7 @@ defmodule Pleroma.Web.Salmon do
|
|||
|> :xmerl.export_simple(:xmerl_xml)
|
||||
|> to_string
|
||||
|
||||
{:ok, private, _} = keys_from_pem(keys)
|
||||
{:ok, private, _} = Keys.keys_from_pem(keys)
|
||||
{:ok, feed} = encode(private, feed)
|
||||
|
||||
remote_users = remote_users(user, activity)
|
||||
|
|
@ -268,7 +230,7 @@ defmodule Pleroma.Web.Salmon do
|
|||
def publish(%{id: id}, _), do: Logger.debug(fn -> "Keys missing for user #{id}" end)
|
||||
|
||||
def gather_webfinger_links(%User{} = user) do
|
||||
{:ok, _private, public} = keys_from_pem(user.info.keys)
|
||||
{:ok, _private, public} = Keys.keys_from_pem(user.info.keys)
|
||||
magic_key = encode_key(public)
|
||||
|
||||
[
|
||||
|
|
|
|||
|
|
@ -284,6 +284,12 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
|
|||
Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
|
||||
)
|
||||
|
||||
thread_muted? =
|
||||
case activity.thread_muted? do
|
||||
thread_muted? when is_boolean(thread_muted?) -> thread_muted?
|
||||
nil -> CommonAPI.thread_muted?(user, activity)
|
||||
end
|
||||
|
||||
%{
|
||||
"id" => activity.id,
|
||||
"uri" => object.data["id"],
|
||||
|
|
@ -314,7 +320,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
|
|||
"summary" => summary,
|
||||
"summary_html" => summary |> Formatter.emojify(object.data["emoji"]),
|
||||
"card" => card,
|
||||
"muted" => CommonAPI.thread_muted?(user, activity) || User.mutes?(opts[:for], user)
|
||||
"muted" => thread_muted? || User.mutes?(opts[:for], user)
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ defmodule Pleroma.Web.WebFinger do
|
|||
alias Pleroma.User
|
||||
alias Pleroma.Web
|
||||
alias Pleroma.Web.Federator.Publisher
|
||||
alias Pleroma.Web.Salmon
|
||||
alias Pleroma.Web.XML
|
||||
alias Pleroma.XmlBuilder
|
||||
require Jason
|
||||
|
|
@ -61,7 +60,7 @@ defmodule Pleroma.Web.WebFinger do
|
|||
end
|
||||
|
||||
def represent_user(user, "JSON") do
|
||||
{:ok, user} = ensure_keys_present(user)
|
||||
{:ok, user} = User.ensure_keys_present(user)
|
||||
|
||||
%{
|
||||
"subject" => "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}",
|
||||
|
|
@ -71,7 +70,7 @@ defmodule Pleroma.Web.WebFinger do
|
|||
end
|
||||
|
||||
def represent_user(user, "XML") do
|
||||
{:ok, user} = ensure_keys_present(user)
|
||||
{:ok, user} = User.ensure_keys_present(user)
|
||||
|
||||
links =
|
||||
gather_links(user)
|
||||
|
|
@ -88,27 +87,6 @@ defmodule Pleroma.Web.WebFinger do
|
|||
|> XmlBuilder.to_doc()
|
||||
end
|
||||
|
||||
# This seems a better fit in Salmon
|
||||
def ensure_keys_present(user) do
|
||||
info = user.info
|
||||
|
||||
if info.keys do
|
||||
{:ok, user}
|
||||
else
|
||||
{:ok, pem} = Salmon.generate_rsa_pem()
|
||||
|
||||
info_cng =
|
||||
info
|
||||
|> User.Info.set_keys(pem)
|
||||
|
||||
cng =
|
||||
Ecto.Changeset.change(user)
|
||||
|> Ecto.Changeset.put_embed(:info, info_cng)
|
||||
|
||||
User.update_and_set_cache(cng)
|
||||
end
|
||||
end
|
||||
|
||||
defp get_magic_key(magic_key) do
|
||||
"data:application/magic-public-key," <> magic_key = magic_key
|
||||
{:ok, magic_key}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue