Merge remote-tracking branch 'origin/develop' into shigusegubu
This commit is contained in:
commit
4e05e7a989
84 changed files with 965 additions and 320 deletions
|
|
@ -9,7 +9,7 @@ defmodule Mix.Tasks.Pleroma.Search.Meilisearch do
|
|||
import Ecto.Query
|
||||
|
||||
import Pleroma.Search.Meilisearch,
|
||||
only: [meili_post: 2, meili_put: 2, meili_get: 1, meili_delete: 1]
|
||||
only: [meili_put: 2, meili_get: 1, meili_delete: 1]
|
||||
|
||||
def run(["index"]) do
|
||||
start_pleroma()
|
||||
|
|
@ -28,7 +28,7 @@ defmodule Mix.Tasks.Pleroma.Search.Meilisearch do
|
|||
end
|
||||
|
||||
{:ok, _} =
|
||||
meili_post(
|
||||
meili_put(
|
||||
"/indexes/objects/settings/ranking-rules",
|
||||
[
|
||||
"published:desc",
|
||||
|
|
@ -42,7 +42,7 @@ defmodule Mix.Tasks.Pleroma.Search.Meilisearch do
|
|||
)
|
||||
|
||||
{:ok, _} =
|
||||
meili_post(
|
||||
meili_put(
|
||||
"/indexes/objects/settings/searchable-attributes",
|
||||
[
|
||||
"content"
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ defmodule Pleroma.Constants do
|
|||
|
||||
const(activity_types,
|
||||
do: [
|
||||
"Block",
|
||||
"Create",
|
||||
"Update",
|
||||
"Delete",
|
||||
|
|
@ -115,6 +116,10 @@ defmodule Pleroma.Constants do
|
|||
]
|
||||
)
|
||||
|
||||
const(object_types,
|
||||
do: ~w[Event Question Answer Audio Video Image Article Note Page ChatMessage]
|
||||
)
|
||||
|
||||
# basic regex, just there to weed out potential mistakes
|
||||
# https://datatracker.ietf.org/doc/html/rfc2045#section-5.1
|
||||
const(mime_regex,
|
||||
|
|
|
|||
|
|
@ -74,11 +74,14 @@ defmodule Pleroma.Frontend do
|
|||
|
||||
new_file_path = Path.join(dest, path)
|
||||
|
||||
new_file_path
|
||||
path
|
||||
|> Path.dirname()
|
||||
|> then(&Path.join(dest, &1))
|
||||
|> File.mkdir_p!()
|
||||
|
||||
File.write!(new_file_path, data)
|
||||
if not File.dir?(new_file_path) do
|
||||
File.write!(new_file_path, data)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -15,6 +15,14 @@ defmodule Pleroma.LDAP do
|
|||
GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
||||
end
|
||||
|
||||
def bind_user(name, password) do
|
||||
GenServer.call(__MODULE__, {:bind_user, name, password})
|
||||
end
|
||||
|
||||
def change_password(name, password, new_password) do
|
||||
GenServer.call(__MODULE__, {:change_password, name, password, new_password})
|
||||
end
|
||||
|
||||
@impl true
|
||||
def init(state) do
|
||||
case {Config.get(Pleroma.Web.Auth.Authenticator), Config.get([:ldap, :enabled])} do
|
||||
|
|
@ -47,13 +55,42 @@ defmodule Pleroma.LDAP do
|
|||
def handle_info(:connect, _state), do: do_handle_connect()
|
||||
|
||||
def handle_info({:bind_after_reconnect, name, password, from}, state) do
|
||||
result = bind_user(state[:handle], name, password)
|
||||
result = do_bind_user(state[:handle], name, password)
|
||||
|
||||
GenServer.reply(from, result)
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call({:bind_user, name, password}, from, state) do
|
||||
case do_bind_user(state[:handle], name, password) do
|
||||
:needs_reconnect ->
|
||||
Process.send(self(), {:bind_after_reconnect, name, password, from}, [])
|
||||
{:noreply, state, {:continue, :connect}}
|
||||
|
||||
result ->
|
||||
{:reply, result, state, :hibernate}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_call({:change_password, name, password, new_password}, _from, state) do
|
||||
result = change_password(state[:handle], name, password, new_password)
|
||||
|
||||
{:reply, result, state, :hibernate}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def terminate(_, state) do
|
||||
handle = Keyword.get(state, :handle)
|
||||
|
||||
if not is_nil(handle) do
|
||||
:eldap.close(handle)
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
defp do_handle_connect do
|
||||
state =
|
||||
case connect() do
|
||||
|
|
@ -71,33 +108,6 @@ defmodule Pleroma.LDAP do
|
|||
{:noreply, state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call({:bind_user, name, password}, from, state) do
|
||||
case bind_user(state[:handle], name, password) do
|
||||
:needs_reconnect ->
|
||||
Process.send(self(), {:bind_after_reconnect, name, password, from}, [])
|
||||
{:noreply, state, {:continue, :connect}}
|
||||
|
||||
result ->
|
||||
{:reply, result, state, :hibernate}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def terminate(_, state) do
|
||||
handle = Keyword.get(state, :handle)
|
||||
|
||||
if not is_nil(handle) do
|
||||
:eldap.close(handle)
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
def bind_user(name, password) do
|
||||
GenServer.call(__MODULE__, {:bind_user, name, password})
|
||||
end
|
||||
|
||||
defp connect do
|
||||
ldap = Config.get(:ldap, [])
|
||||
host = Keyword.get(ldap, :host, "localhost")
|
||||
|
|
@ -161,18 +171,17 @@ defmodule Pleroma.LDAP do
|
|||
end
|
||||
end
|
||||
|
||||
defp bind_user(handle, name, password) do
|
||||
uid = Config.get([:ldap, :uid], "cn")
|
||||
base = Config.get([:ldap, :base])
|
||||
defp do_bind_user(handle, name, password) do
|
||||
dn = make_dn(name)
|
||||
|
||||
case :eldap.simple_bind(handle, "#{uid}=#{name},#{base}", password) do
|
||||
case :eldap.simple_bind(handle, dn, password) do
|
||||
:ok ->
|
||||
case fetch_user(name) do
|
||||
%User{} = user ->
|
||||
user
|
||||
|
||||
_ ->
|
||||
register_user(handle, base, uid, name)
|
||||
register_user(handle, ldap_base(), ldap_uid(), name)
|
||||
end
|
||||
|
||||
# eldap does not inform us of socket closure
|
||||
|
|
@ -231,6 +240,14 @@ defmodule Pleroma.LDAP do
|
|||
end
|
||||
end
|
||||
|
||||
defp change_password(handle, name, password, new_password) do
|
||||
dn = make_dn(name)
|
||||
|
||||
with :ok <- :eldap.simple_bind(handle, dn, password) do
|
||||
:eldap.modify_password(handle, dn, to_charlist(new_password), to_charlist(password))
|
||||
end
|
||||
end
|
||||
|
||||
defp decode_certfile(file) do
|
||||
with {:ok, data} <- File.read(file) do
|
||||
data
|
||||
|
|
@ -242,4 +259,13 @@ defmodule Pleroma.LDAP do
|
|||
[]
|
||||
end
|
||||
end
|
||||
|
||||
defp ldap_uid, do: to_charlist(Config.get([:ldap, :uid], "cn"))
|
||||
defp ldap_base, do: to_charlist(Config.get([:ldap, :base]))
|
||||
|
||||
defp make_dn(name) do
|
||||
uid = ldap_uid()
|
||||
base = ldap_base()
|
||||
~c"#{uid}=#{name},#{base}"
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -99,27 +99,6 @@ defmodule Pleroma.Object do
|
|||
def get_by_id(nil), do: nil
|
||||
def get_by_id(id), do: Repo.get(Object, id)
|
||||
|
||||
@spec get_by_id_and_maybe_refetch(integer(), list()) :: Object.t() | nil
|
||||
def get_by_id_and_maybe_refetch(id, opts \\ []) do
|
||||
with %Object{updated_at: updated_at} = object <- get_by_id(id) do
|
||||
if opts[:interval] &&
|
||||
NaiveDateTime.diff(NaiveDateTime.utc_now(), updated_at) > opts[:interval] do
|
||||
case Fetcher.refetch_object(object) do
|
||||
{:ok, %Object{} = object} ->
|
||||
object
|
||||
|
||||
e ->
|
||||
Logger.error("Couldn't refresh #{object.data["id"]}:\n#{inspect(e)}")
|
||||
object
|
||||
end
|
||||
else
|
||||
object
|
||||
end
|
||||
else
|
||||
nil -> nil
|
||||
end
|
||||
end
|
||||
|
||||
def get_by_ap_id(nil), do: nil
|
||||
|
||||
def get_by_ap_id(ap_id) do
|
||||
|
|
|
|||
|
|
@ -16,17 +16,24 @@ defmodule Pleroma.ReleaseTasks do
|
|||
end
|
||||
end
|
||||
|
||||
def find_module(task) do
|
||||
module_name =
|
||||
task
|
||||
|> String.split(".")
|
||||
|> Enum.map(&String.capitalize/1)
|
||||
|> then(fn x -> [Mix, Tasks, Pleroma] ++ x end)
|
||||
|> Module.concat()
|
||||
|
||||
case Code.ensure_loaded(module_name) do
|
||||
{:module, _} -> module_name
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
defp mix_task(task, args) do
|
||||
Application.load(:pleroma)
|
||||
{:ok, modules} = :application.get_key(:pleroma, :modules)
|
||||
|
||||
module =
|
||||
Enum.find(modules, fn module ->
|
||||
module = Module.split(module)
|
||||
|
||||
match?(["Mix", "Tasks", "Pleroma" | _], module) and
|
||||
String.downcase(List.last(module)) == task
|
||||
end)
|
||||
module = find_module(task)
|
||||
|
||||
if module do
|
||||
module.run(args)
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ defmodule Pleroma.Search.Meilisearch do
|
|||
# Only index public or unlisted Notes
|
||||
if not is_nil(object) and object.data["type"] == "Note" and
|
||||
not is_nil(object.data["content"]) and
|
||||
not is_nil(object.data["published"]) and
|
||||
(Pleroma.Constants.as_public() in object.data["to"] or
|
||||
Pleroma.Constants.as_public() in object.data["cc"]) and
|
||||
object.data["content"] not in ["", "."] do
|
||||
|
|
|
|||
|
|
@ -17,8 +17,16 @@ defmodule Pleroma.Upload.Filter.Dedupe do
|
|||
|> Base.encode16(case: :lower)
|
||||
|
||||
filename = shasum <> "." <> extension
|
||||
{:ok, :filtered, %Upload{upload | id: shasum, path: filename}}
|
||||
|
||||
{:ok, :filtered, %Upload{upload | id: shasum, path: shard_path(filename)}}
|
||||
end
|
||||
|
||||
def filter(_), do: {:ok, :noop}
|
||||
|
||||
@spec shard_path(String.t()) :: String.t()
|
||||
def shard_path(
|
||||
<<a::binary-size(2), b::binary-size(2), c::binary-size(2), _::binary>> = filename
|
||||
) do
|
||||
Path.join([a, b, c, filename])
|
||||
end
|
||||
end
|
||||
|
|
|
|||
118
lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex
Normal file
118
lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy do
|
||||
@moduledoc "Drop remote reports if they don't contain enough information."
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
alias Pleroma.Config
|
||||
|
||||
@impl true
|
||||
def filter(%{"type" => "Flag"} = object) do
|
||||
with {_, false} <- {:local, local?(object)},
|
||||
{:ok, _} <- maybe_reject_all(object),
|
||||
{:ok, _} <- maybe_reject_anonymous(object),
|
||||
{:ok, _} <- maybe_reject_third_party(object),
|
||||
{:ok, _} <- maybe_reject_empty_message(object) do
|
||||
{:ok, object}
|
||||
else
|
||||
{:local, true} -> {:ok, object}
|
||||
{:reject, message} -> {:reject, message}
|
||||
error -> {:reject, error}
|
||||
end
|
||||
end
|
||||
|
||||
def filter(object), do: {:ok, object}
|
||||
|
||||
defp maybe_reject_all(object) do
|
||||
if Config.get([:mrf_remote_report, :reject_all]) do
|
||||
{:reject, "[RemoteReportPolicy] Remote report"}
|
||||
else
|
||||
{:ok, object}
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_reject_anonymous(%{"actor" => actor} = object) do
|
||||
with true <- Config.get([:mrf_remote_report, :reject_anonymous]),
|
||||
%URI{path: "/actor"} <- URI.parse(actor) do
|
||||
{:reject, "[RemoteReportPolicy] Anonymous: #{actor}"}
|
||||
else
|
||||
_ -> {:ok, object}
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_reject_third_party(%{"object" => objects} = object) do
|
||||
{_, to} =
|
||||
case objects do
|
||||
[head | tail] when is_binary(head) -> {tail, head}
|
||||
s when is_binary(s) -> {[], s}
|
||||
_ -> {[], ""}
|
||||
end
|
||||
|
||||
with true <- Config.get([:mrf_remote_report, :reject_third_party]),
|
||||
false <- String.starts_with?(to, Pleroma.Web.Endpoint.url()) do
|
||||
{:reject, "[RemoteReportPolicy] Third-party: #{to}"}
|
||||
else
|
||||
_ -> {:ok, object}
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_reject_empty_message(%{"content" => content} = object)
|
||||
when is_binary(content) and content != "" do
|
||||
{:ok, object}
|
||||
end
|
||||
|
||||
defp maybe_reject_empty_message(object) do
|
||||
if Config.get([:mrf_remote_report, :reject_empty_message]) do
|
||||
{:reject, ["RemoteReportPolicy] No content"]}
|
||||
else
|
||||
{:ok, object}
|
||||
end
|
||||
end
|
||||
|
||||
defp local?(%{"actor" => actor}) do
|
||||
String.starts_with?(actor, Pleroma.Web.Endpoint.url())
|
||||
end
|
||||
|
||||
@impl true
|
||||
def describe do
|
||||
mrf_remote_report =
|
||||
Config.get(:mrf_remote_report)
|
||||
|> Enum.into(%{})
|
||||
|
||||
{:ok, %{mrf_remote_report: mrf_remote_report}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_remote_report,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy",
|
||||
label: "MRF Remote Report",
|
||||
description: "Drop remote reports if they don't contain enough information.",
|
||||
children: [
|
||||
%{
|
||||
key: :reject_all,
|
||||
type: :boolean,
|
||||
description: "Reject all remote reports? (this option takes precedence)",
|
||||
suggestions: [false]
|
||||
},
|
||||
%{
|
||||
key: :reject_anonymous,
|
||||
type: :boolean,
|
||||
description: "Reject anonymous remote reports?",
|
||||
suggestions: [true]
|
||||
},
|
||||
%{
|
||||
key: :reject_third_party,
|
||||
type: :boolean,
|
||||
description: "Reject reports on users from third-party instances?",
|
||||
suggestions: [true]
|
||||
},
|
||||
%{
|
||||
key: :reject_empty_message,
|
||||
type: :boolean,
|
||||
description: "Reject remote reports with no message?",
|
||||
suggestions: [true]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -11,6 +11,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
|||
|
||||
@behaviour Pleroma.Web.ActivityPub.ObjectValidator.Validating
|
||||
|
||||
import Pleroma.Constants, only: [activity_types: 0, object_types: 0]
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||
alias Pleroma.Object
|
||||
|
|
@ -38,6 +40,16 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
|||
@impl true
|
||||
def validate(object, meta)
|
||||
|
||||
# This overload works together with the InboxGuardPlug
|
||||
# and ensures that we are not accepting any activity type
|
||||
# that cannot pass InboxGuardPlug.
|
||||
# If we want to support any more activity types, make sure to
|
||||
# add it in Pleroma.Constants's activity_types or object_types,
|
||||
# and, if applicable, allowed_activity_types_from_strangers.
|
||||
def validate(%{"type" => type}, _meta)
|
||||
when type not in activity_types() and type not in object_types(),
|
||||
do: {:error, :not_allowed_object_type}
|
||||
|
||||
def validate(%{"type" => "Block"} = block_activity, meta) do
|
||||
with {:ok, block_activity} <-
|
||||
block_activity
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ defmodule Pleroma.Web.ApiSpec.MediaOperation do
|
|||
security: [%{"oAuth" => ["write:media"]}],
|
||||
requestBody: Helpers.request_body("Parameters", create_request()),
|
||||
responses: %{
|
||||
202 => Operation.response("Media", "application/json", Attachment),
|
||||
200 => 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)
|
||||
|
|
|
|||
|
|
@ -10,4 +10,9 @@ defmodule Pleroma.Web.Auth.Authenticator do
|
|||
@callback handle_error(Plug.Conn.t(), any()) :: any()
|
||||
@callback auth_template() :: String.t() | nil
|
||||
@callback oauth_consumer_template() :: String.t() | nil
|
||||
|
||||
@callback change_password(Pleroma.User.t(), String.t(), String.t(), String.t()) ::
|
||||
{:ok, Pleroma.User.t()} | {:error, term()}
|
||||
|
||||
@optional_callbacks change_password: 4
|
||||
end
|
||||
|
|
|
|||
|
|
@ -30,4 +30,13 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do
|
|||
error
|
||||
end
|
||||
end
|
||||
|
||||
def change_password(user, password, new_password, new_password) do
|
||||
case LDAP.change_password(user.nickname, password, new_password) do
|
||||
:ok -> {:ok, user}
|
||||
e -> e
|
||||
end
|
||||
end
|
||||
|
||||
def change_password(_, _, _, _), do: {:error, :password_confirmation}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
|
|||
alias Pleroma.Registration
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.Plugs.AuthenticationPlug
|
||||
|
||||
import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1, fetch_user: 1]
|
||||
|
|
@ -101,4 +102,23 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
|
|||
def auth_template, do: nil
|
||||
|
||||
def oauth_consumer_template, do: nil
|
||||
|
||||
@doc "Changes Pleroma.User password in the database"
|
||||
def change_password(user, password, new_password, new_password) do
|
||||
case CommonAPI.Utils.confirm_current_password(user, password) do
|
||||
{:ok, user} ->
|
||||
with {:ok, _user} <-
|
||||
User.reset_password(user, %{
|
||||
password: new_password,
|
||||
password_confirmation: new_password
|
||||
}) do
|
||||
{:ok, user}
|
||||
end
|
||||
|
||||
error ->
|
||||
error
|
||||
end
|
||||
end
|
||||
|
||||
def change_password(_, _, _, _), do: {:error, :password_confirmation}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -39,4 +39,8 @@ defmodule Pleroma.Web.Auth.WrapperAuthenticator do
|
|||
implementation().oauth_consumer_template() ||
|
||||
Pleroma.Config.get([:auth, :oauth_consumer_template], "consumer.html")
|
||||
end
|
||||
|
||||
@impl true
|
||||
def change_password(user, password, new_password, new_password_confirmation),
|
||||
do: implementation().change_password(user, password, new_password, new_password_confirmation)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ defmodule Pleroma.Web.Endpoint do
|
|||
websocket: [
|
||||
path: "/",
|
||||
compress: false,
|
||||
connect_info: [:sec_websocket_protocol],
|
||||
error_handler: {Pleroma.Web.MastodonAPI.WebsocketHandler, :handle_error, []},
|
||||
fullsweep_after: 20
|
||||
]
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ defmodule Pleroma.Web.Fallback.RedirectController do
|
|||
redirector_with_meta(conn, %{user: user})
|
||||
else
|
||||
nil ->
|
||||
redirector(conn, params)
|
||||
redirector_with_meta(conn, Map.delete(params, "maybe_nickname_or_id"))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ defmodule Pleroma.Web.Feed.TagController do
|
|||
alias Pleroma.Web.Feed.FeedView
|
||||
|
||||
def feed(conn, params) do
|
||||
if Config.get!([:instance, :public]) do
|
||||
if not Config.restrict_unauthenticated_access?(:timelines, :local) do
|
||||
render_feed(conn, params)
|
||||
else
|
||||
render_error(conn, :not_found, "Not found")
|
||||
|
|
@ -18,10 +18,12 @@ defmodule Pleroma.Web.Feed.TagController do
|
|||
end
|
||||
|
||||
defp render_feed(conn, %{"tag" => raw_tag} = params) do
|
||||
local_only = Config.restrict_unauthenticated_access?(:timelines, :federated)
|
||||
|
||||
{format, tag} = parse_tag(raw_tag)
|
||||
|
||||
activities =
|
||||
%{type: ["Create"], tag: tag}
|
||||
%{type: ["Create"], tag: tag, local_only: local_only}
|
||||
|> Pleroma.Maps.put_if_present(:max_id, params["max_id"])
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@ defmodule Pleroma.Web.Feed.UserController do
|
|||
|
||||
action_fallback(:errors)
|
||||
|
||||
def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname}) do
|
||||
def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname} = params) do
|
||||
with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname_or_id(nickname)} do
|
||||
Pleroma.Web.Fallback.RedirectController.redirector_with_meta(conn, %{user: user})
|
||||
else
|
||||
_ -> Pleroma.Web.Fallback.RedirectController.redirector(conn, nil)
|
||||
_ -> Pleroma.Web.Fallback.RedirectController.redirector_with_meta(conn, params)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -53,9 +53,7 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do
|
|||
) do
|
||||
attachment_data = Map.put(object.data, "id", object.id)
|
||||
|
||||
conn
|
||||
|> put_status(202)
|
||||
|> render("attachment.json", %{attachment: attachment_data})
|
||||
render(conn, "attachment.json", %{attachment: attachment_data})
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ defmodule Pleroma.Web.MastodonAPI.PollController do
|
|||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Workers.PollWorker
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
|
|
@ -27,12 +28,16 @@ defmodule Pleroma.Web.MastodonAPI.PollController do
|
|||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PollOperation
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
@poll_refresh_interval 120
|
||||
|
||||
@doc "GET /api/v1/polls/:id"
|
||||
def show(%{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn, _) do
|
||||
with %Object{} = object <- Object.get_by_id_and_maybe_refetch(id, interval: 60),
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
|
||||
with %Object{} = object <- Object.get_by_id(id),
|
||||
%Activity{} = activity <-
|
||||
Activity.get_create_by_object_ap_id_with_object(object.data["id"]),
|
||||
true <- Visibility.visible_for_user?(activity, user) do
|
||||
maybe_refresh_poll(activity)
|
||||
|
||||
try_render(conn, "show.json", %{object: object, for: user})
|
||||
else
|
||||
error when is_nil(error) or error == false ->
|
||||
|
|
@ -70,4 +75,13 @@ defmodule Pleroma.Web.MastodonAPI.PollController do
|
|||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp maybe_refresh_poll(%Activity{object: %Object{} = object} = activity) do
|
||||
with false <- activity.local,
|
||||
{:ok, end_time} <- NaiveDateTime.from_iso8601(object.data["closed"]),
|
||||
{_, :lt} <- {:closed_compare, NaiveDateTime.compare(object.updated_at, end_time)} do
|
||||
PollWorker.new(%{"op" => "refresh", "activity_id" => activity.id})
|
||||
|> Oban.insert(unique: [period: @poll_refresh_interval])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
|
|||
# This only prepares the connection and is not in the process yet
|
||||
@impl Phoenix.Socket.Transport
|
||||
def connect(%{params: params} = transport_info) do
|
||||
with access_token <- Map.get(params, "access_token"),
|
||||
with access_token <- find_access_token(transport_info),
|
||||
{:ok, user, oauth_token} <- authenticate_request(access_token),
|
||||
{:ok, topic} <-
|
||||
Streamer.get_topic(params["stream"], user, oauth_token, params) do
|
||||
|
|
@ -244,4 +244,13 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
|
|||
def handle_error(conn, _reason) do
|
||||
Plug.Conn.send_resp(conn, 404, "Not Found")
|
||||
end
|
||||
|
||||
defp find_access_token(%{
|
||||
connect_info: %{sec_websocket_protocol: [token]}
|
||||
}),
|
||||
do: token
|
||||
|
||||
defp find_access_token(%{params: %{"access_token" => token}}), do: token
|
||||
|
||||
defp find_access_token(_), do: nil
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.Metadata do
|
|||
|
||||
def build_tags(params) do
|
||||
providers = [
|
||||
Pleroma.Web.Metadata.Providers.ActivityPub,
|
||||
Pleroma.Web.Metadata.Providers.RelMe,
|
||||
Pleroma.Web.Metadata.Providers.RestrictIndexing
|
||||
| activated_providers()
|
||||
|
|
|
|||
22
lib/pleroma/web/metadata/providers/activity_pub.ex
Normal file
22
lib/pleroma/web/metadata/providers/activity_pub.ex
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Metadata.Providers.ActivityPub do
|
||||
alias Pleroma.Web.Metadata.Providers.Provider
|
||||
|
||||
@behaviour Provider
|
||||
|
||||
@impl Provider
|
||||
def build_tags(%{object: %{data: %{"id" => object_id}}}) do
|
||||
[{:link, [rel: "alternate", type: "application/activity+json", href: object_id], []}]
|
||||
end
|
||||
|
||||
@impl Provider
|
||||
def build_tags(%{user: user}) do
|
||||
[{:link, [rel: "alternate", type: "application/activity+json", href: user.ap_id], []}]
|
||||
end
|
||||
|
||||
@impl Provider
|
||||
def build_tags(_), do: []
|
||||
end
|
||||
|
|
@ -10,7 +10,7 @@ defmodule Pleroma.Web.Metadata.Providers.Feed do
|
|||
@behaviour Provider
|
||||
|
||||
@impl Provider
|
||||
def build_tags(%{user: user}) do
|
||||
def build_tags(%{user: %{local: true} = user}) do
|
||||
[
|
||||
{:link,
|
||||
[
|
||||
|
|
@ -20,4 +20,7 @@ defmodule Pleroma.Web.Metadata.Providers.Feed do
|
|||
], []}
|
||||
]
|
||||
end
|
||||
|
||||
@impl Provider
|
||||
def build_tags(_), do: []
|
||||
end
|
||||
|
|
|
|||
|
|
@ -67,6 +67,9 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
|
|||
end
|
||||
end
|
||||
|
||||
@impl Provider
|
||||
def build_tags(_), do: []
|
||||
|
||||
defp build_attachments(%{data: %{"attachment" => attachments}}) do
|
||||
Enum.reduce(attachments, [], fn attachment, acc ->
|
||||
rendered_tags =
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ defmodule Pleroma.Web.Metadata.Providers.RelMe do
|
|||
end)
|
||||
end
|
||||
|
||||
@impl Provider
|
||||
def build_tags(_), do: []
|
||||
|
||||
defp append_fields_tag(bio, fields) do
|
||||
fields
|
||||
|> Enum.reduce(bio, fn %{"value" => v}, res -> res <> v end)
|
||||
|
|
|
|||
|
|
@ -44,6 +44,9 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
|
|||
end
|
||||
end
|
||||
|
||||
@impl Provider
|
||||
def build_tags(_), do: []
|
||||
|
||||
defp title_tag(user) do
|
||||
{:meta, [name: "twitter:title", content: Utils.user_name_string(user)], []}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ defmodule Pleroma.Web.Push do
|
|||
end
|
||||
|
||||
def vapid_config do
|
||||
Application.get_env(:web_push_encryption, :vapid_details, nil)
|
||||
Application.get_env(:web_push_encryption, :vapid_details, [])
|
||||
end
|
||||
|
||||
def enabled, do: match?([subject: _, public_key: _, private_key: _], vapid_config())
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
alias Pleroma.Healthcheck
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.Auth.WrapperAuthenticator, as: Authenticator
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Web.WebFinger
|
||||
|
|
@ -195,19 +196,21 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
%{assigns: %{user: user}, private: %{open_api_spex: %{body_params: body_params}}} = conn,
|
||||
_
|
||||
) do
|
||||
case CommonAPI.Utils.confirm_current_password(user, body_params.password) do
|
||||
{:ok, user} ->
|
||||
with {:ok, _user} <-
|
||||
User.reset_password(user, %{
|
||||
password: body_params.new_password,
|
||||
password_confirmation: body_params.new_password_confirmation
|
||||
}) do
|
||||
json(conn, %{status: "success"})
|
||||
else
|
||||
{:error, changeset} ->
|
||||
{_, {error, _}} = Enum.at(changeset.errors, 0)
|
||||
json(conn, %{error: "New password #{error}."})
|
||||
end
|
||||
with {:ok, %User{}} <-
|
||||
Authenticator.change_password(
|
||||
user,
|
||||
body_params.password,
|
||||
body_params.new_password,
|
||||
body_params.new_password_confirmation
|
||||
) do
|
||||
json(conn, %{status: "success"})
|
||||
else
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
{_, {error, _}} = Enum.at(changeset.errors, 0)
|
||||
json(conn, %{error: "New password #{error}."})
|
||||
|
||||
{:error, :password_confirmation} ->
|
||||
json(conn, %{error: "New password does not match confirmation."})
|
||||
|
||||
{:error, msg} ->
|
||||
json(conn, %{error: msg})
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ defmodule Pleroma.Web.TwitterAPI.TokenView do
|
|||
%{
|
||||
id: token_entry.id,
|
||||
valid_until: token_entry.valid_until,
|
||||
app_name: token_entry.app.client_name
|
||||
app_name: token_entry.app.client_name,
|
||||
scopes: token_entry.scopes
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,27 +11,46 @@ defmodule Pleroma.Workers.PollWorker do
|
|||
alias Pleroma.Activity
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Object.Fetcher
|
||||
|
||||
@stream_out_impl Pleroma.Config.get(
|
||||
[__MODULE__, :stream_out],
|
||||
Pleroma.Web.ActivityPub.ActivityPub
|
||||
)
|
||||
|
||||
@impl true
|
||||
def perform(%Job{args: %{"op" => "poll_end", "activity_id" => activity_id}}) do
|
||||
with %Activity{} = activity <- find_poll_activity(activity_id),
|
||||
with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id(activity_id)},
|
||||
{:ok, notifications} <- Notification.create_poll_notifications(activity) do
|
||||
unless activity.local do
|
||||
# Schedule a final refresh
|
||||
__MODULE__.new(%{"op" => "refresh", "activity_id" => activity_id})
|
||||
|> Oban.insert()
|
||||
end
|
||||
|
||||
Notification.stream(notifications)
|
||||
else
|
||||
{:error, :poll_activity_not_found} = e -> {:cancel, e}
|
||||
{:activity, nil} -> {:cancel, :poll_activity_not_found}
|
||||
e -> {:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
def perform(%Job{args: %{"op" => "refresh", "activity_id" => activity_id}}) do
|
||||
with {_, %Activity{object: object}} <-
|
||||
{:activity, Activity.get_by_id_with_object(activity_id)},
|
||||
{_, {:ok, _object}} <- {:refetch, Fetcher.refetch_object(object)} do
|
||||
stream_update(activity_id)
|
||||
|
||||
:ok
|
||||
else
|
||||
{:activity, nil} -> {:cancel, :poll_activity_not_found}
|
||||
{:refetch, _} = e -> {:cancel, e}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def timeout(_job), do: :timer.seconds(5)
|
||||
|
||||
defp find_poll_activity(activity_id) do
|
||||
with nil <- Activity.get_by_id(activity_id) do
|
||||
{:error, :poll_activity_not_found}
|
||||
end
|
||||
end
|
||||
|
||||
def schedule_poll_end(%Activity{data: %{"type" => "Create"}, id: activity_id} = activity) do
|
||||
with %Object{data: %{"type" => "Question", "closed" => closed}} when is_binary(closed) <-
|
||||
Object.normalize(activity),
|
||||
|
|
@ -49,4 +68,10 @@ defmodule Pleroma.Workers.PollWorker do
|
|||
end
|
||||
|
||||
def schedule_poll_end(activity), do: {:error, activity}
|
||||
|
||||
defp stream_update(activity_id) do
|
||||
Activity.get_by_id(activity_id)
|
||||
|> Activity.normalize()
|
||||
|> @stream_out_impl.stream_out()
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue