Merge branch 'develop' into feature/mstdn-direct-api
This commit is contained in:
commit
4fd9df100f
99 changed files with 1993 additions and 139 deletions
13
lib/mix/tasks/deactivate_user.ex
Normal file
13
lib/mix/tasks/deactivate_user.ex
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
defmodule Mix.Tasks.DeactivateUser do
|
||||
use Mix.Task
|
||||
alias Pleroma.User
|
||||
|
||||
@shortdoc "Toggle deactivation status for a user"
|
||||
def run([nickname]) do
|
||||
Mix.Task.run("app.start")
|
||||
|
||||
with user <- User.get_by_nickname(nickname) do
|
||||
User.deactivate(user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -6,3 +6,4 @@ ALTER DATABASE pleroma_dev OWNER TO pleroma;
|
|||
\c pleroma_dev;
|
||||
--Extensions made by ecto.migrate that need superuser access
|
||||
CREATE EXTENSION IF NOT EXISTS citext;
|
||||
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
||||
|
|
|
|||
|
|
@ -160,6 +160,7 @@ defmodule Pleroma.Formatter do
|
|||
links =
|
||||
Regex.scan(@link_regex, text)
|
||||
|> Enum.map(fn [url] -> {Ecto.UUID.generate(), url} end)
|
||||
|> Enum.sort_by(fn {_, url} -> -String.length(url) end)
|
||||
|
||||
uuid_text =
|
||||
links
|
||||
|
|
|
|||
|
|
@ -91,7 +91,8 @@ defmodule Pleroma.Notification do
|
|||
|
||||
# TODO move to sql, too.
|
||||
def create_notification(%Activity{} = activity, %User{} = user) do
|
||||
unless User.blocks?(user, %{ap_id: activity.data["actor"]}) do
|
||||
unless User.blocks?(user, %{ap_id: activity.data["actor"]}) or
|
||||
user.ap_id == activity.data["actor"] do
|
||||
notification = %Notification{user_id: user.id, activity: activity}
|
||||
{:ok, notification} = Repo.insert(notification)
|
||||
Pleroma.Web.Streamer.stream("user", notification)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
|
||||
alias Pleroma.Web.HTTPSignatures
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
import Plug.Conn
|
||||
require Logger
|
||||
|
||||
|
|
@ -12,7 +13,7 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
|
|||
end
|
||||
|
||||
def call(conn, _opts) do
|
||||
user = conn.params["actor"]
|
||||
user = Utils.normalize_actor(conn.params["actor"])
|
||||
Logger.debug("Checking sig for #{user}")
|
||||
[signature | _] = get_req_header(conn, "signature")
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ defmodule Pleroma.User do
|
|||
field(:local, :boolean, default: true)
|
||||
field(:info, :map, default: %{})
|
||||
field(:follower_address, :string)
|
||||
field(:search_distance, :float, virtual: true)
|
||||
has_many(:notifications, Notification)
|
||||
|
||||
timestamps()
|
||||
|
|
@ -399,16 +400,24 @@ defmodule Pleroma.User do
|
|||
User.get_or_fetch_by_nickname(query)
|
||||
end
|
||||
|
||||
q =
|
||||
inner =
|
||||
from(
|
||||
u in User,
|
||||
where:
|
||||
fragment(
|
||||
"(to_tsvector('english', ?) || to_tsvector('english', ?)) @@ plainto_tsquery('english', ?)",
|
||||
u.nickname,
|
||||
u.name,
|
||||
^query
|
||||
),
|
||||
select_merge: %{
|
||||
search_distance:
|
||||
fragment(
|
||||
"? <-> (? || ?)",
|
||||
^query,
|
||||
u.nickname,
|
||||
u.name
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
q =
|
||||
from(
|
||||
s in subquery(inner),
|
||||
order_by: s.search_distance,
|
||||
limit: 20
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
alias Pleroma.{Activity, Repo, Object, Upload, User, Notification}
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.ActivityPub.{Transmogrifier, MRF}
|
||||
alias Pleroma.Web.WebFinger
|
||||
alias Pleroma.Web.Federator
|
||||
alias Pleroma.Web.OStatus
|
||||
|
|
@ -11,16 +11,29 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
@httpoison Application.get_env(:pleroma, :httpoison)
|
||||
|
||||
@instance Application.get_env(:pleroma, :instance)
|
||||
@rewrite_policy Keyword.get(@instance, :rewrite_policy)
|
||||
|
||||
def get_recipients(data) do
|
||||
(data["to"] || []) ++ (data["cc"] || [])
|
||||
end
|
||||
|
||||
defp check_actor_is_active(actor) do
|
||||
if not is_nil(actor) do
|
||||
with user <- User.get_cached_by_ap_id(actor),
|
||||
nil <- user.info["deactivated"] do
|
||||
:ok
|
||||
else
|
||||
_e -> :reject
|
||||
end
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
def insert(map, local \\ true) when is_map(map) do
|
||||
with nil <- Activity.get_by_ap_id(map["id"]),
|
||||
map <- lazy_put_activity_defaults(map),
|
||||
{:ok, map} <- @rewrite_policy.filter(map),
|
||||
:ok <- check_actor_is_active(map["actor"]),
|
||||
{:ok, map} <- MRF.filter(map),
|
||||
:ok <- insert_full_object(map) do
|
||||
{:ok, activity} =
|
||||
Repo.insert(%Activity{
|
||||
|
|
@ -127,11 +140,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
end
|
||||
end
|
||||
|
||||
def unlike(%User{} = actor, %Object{} = object) do
|
||||
with %Activity{} = activity <- get_existing_like(actor.ap_id, object),
|
||||
{:ok, _activity} <- Repo.delete(activity),
|
||||
{:ok, object} <- remove_like_from_object(activity, object) do
|
||||
{:ok, object}
|
||||
def unlike(
|
||||
%User{} = actor,
|
||||
%Object{} = object,
|
||||
activity_id \\ nil,
|
||||
local \\ true
|
||||
) do
|
||||
with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
|
||||
unlike_data <- make_unlike_data(actor, like_activity, activity_id),
|
||||
{:ok, unlike_activity} <- insert(unlike_data, local),
|
||||
{:ok, _activity} <- Repo.delete(like_activity),
|
||||
{:ok, object} <- remove_like_from_object(like_activity, object),
|
||||
:ok <- maybe_federate(unlike_activity) do
|
||||
{:ok, unlike_activity, like_activity, object}
|
||||
else
|
||||
_e -> {:ok, object}
|
||||
end
|
||||
|
|
@ -154,6 +175,24 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
end
|
||||
end
|
||||
|
||||
def unannounce(
|
||||
%User{} = actor,
|
||||
%Object{} = object,
|
||||
activity_id \\ nil,
|
||||
local \\ true
|
||||
) do
|
||||
with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
|
||||
unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
|
||||
{:ok, unannounce_activity} <- insert(unannounce_data, local),
|
||||
:ok <- maybe_federate(unannounce_activity),
|
||||
{:ok, _activity} <- Repo.delete(announce_activity),
|
||||
{:ok, object} <- remove_announce_from_object(announce_activity, object) do
|
||||
{:ok, unannounce_activity, announce_activity, object}
|
||||
else
|
||||
_e -> {:ok, object}
|
||||
end
|
||||
end
|
||||
|
||||
def follow(follower, followed, activity_id \\ nil, local \\ true) do
|
||||
with data <- make_follow_data(follower, followed, activity_id),
|
||||
{:ok, activity} <- insert(data, local),
|
||||
|
|
@ -221,11 +260,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
Repo.all(query)
|
||||
end
|
||||
|
||||
# TODO: Make this work properly with unlisted.
|
||||
def fetch_public_activities(opts \\ %{}) do
|
||||
q = fetch_activities_query(["https://www.w3.org/ns/activitystreams#Public"], opts)
|
||||
|
||||
q
|
||||
|> restrict_unlisted()
|
||||
|> Repo.all()
|
||||
|> Enum.reverse()
|
||||
end
|
||||
|
|
@ -254,6 +293,25 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
defp restrict_visibility(query, _visibility), do: query
|
||||
|
||||
def fetch_user_activities(user, reading_user, params \\ %{}) do
|
||||
params =
|
||||
params
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("actor_id", user.ap_id)
|
||||
|> Map.put("whole_db", true)
|
||||
|
||||
recipients =
|
||||
if reading_user do
|
||||
["https://www.w3.org/ns/activitystreams#Public"] ++
|
||||
[reading_user.ap_id | reading_user.following]
|
||||
else
|
||||
["https://www.w3.org/ns/activitystreams#Public"]
|
||||
end
|
||||
|
||||
fetch_activities(recipients, params)
|
||||
|> Enum.reverse()
|
||||
end
|
||||
|
||||
defp restrict_since(query, %{"since_id" => since_id}) do
|
||||
from(activity in query, where: activity.id > ^since_id)
|
||||
end
|
||||
|
|
@ -356,6 +414,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
defp restrict_blocked(query, _), do: query
|
||||
|
||||
defp restrict_unlisted(query) do
|
||||
from(
|
||||
activity in query,
|
||||
where:
|
||||
fragment(
|
||||
"not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
|
||||
activity.data,
|
||||
^["https://www.w3.org/ns/activitystreams#Public"]
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
def fetch_activities_query(recipients, opts \\ %{}) do
|
||||
base_query =
|
||||
from(
|
||||
|
|
@ -406,6 +476,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
"url" => [%{"href" => data["image"]["url"]}]
|
||||
}
|
||||
|
||||
data = Transmogrifier.maybe_fix_user_object(data)
|
||||
|
||||
user_data = %{
|
||||
ap_id: data["id"],
|
||||
info: %{
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
Logger.info("Signature not from author, relayed message, fetching from source")
|
||||
ActivityPub.fetch_object_from_id(params["object"]["id"])
|
||||
else
|
||||
Logger.info("Signature error")
|
||||
Logger.info("Signature error - make sure you are forwarding the HTTP Host header!")
|
||||
Logger.info("Could not validate #{params["actor"]}")
|
||||
Logger.info(inspect(conn.req_headers))
|
||||
end
|
||||
|
|
|
|||
24
lib/pleroma/web/activity_pub/mrf.ex
Normal file
24
lib/pleroma/web/activity_pub/mrf.ex
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
defmodule Pleroma.Web.ActivityPub.MRF do
|
||||
@callback filter(Map.t()) :: {:ok | :reject, Map.t()}
|
||||
|
||||
def filter(object) do
|
||||
get_policies()
|
||||
|> Enum.reduce({:ok, object}, fn
|
||||
policy, {:ok, object} ->
|
||||
policy.filter(object)
|
||||
|
||||
_, error ->
|
||||
error
|
||||
end)
|
||||
end
|
||||
|
||||
def get_policies() do
|
||||
Application.get_env(:pleroma, :instance, [])
|
||||
|> Keyword.get(:rewrite_policy, [])
|
||||
|> get_policies()
|
||||
end
|
||||
|
||||
defp get_policies(policy) when is_atom(policy), do: [policy]
|
||||
defp get_policies(policies) when is_list(policies), do: policies
|
||||
defp get_policies(_), do: []
|
||||
end
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do
|
||||
require Logger
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
@impl true
|
||||
def filter(object) do
|
||||
Logger.info("REJECTING #{inspect(object)}")
|
||||
{:reject, object}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
@impl true
|
||||
def filter(object) do
|
||||
{:ok, object}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
||||
alias Pleroma.User
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
@mrf_policy Application.get_env(:pleroma, :mrf_simple)
|
||||
|
||||
|
|
@ -69,6 +70,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
|||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def filter(object) do
|
||||
actor_info = URI.parse(object["actor"])
|
||||
|
||||
|
|
|
|||
|
|
@ -223,9 +223,45 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
end
|
||||
end
|
||||
|
||||
def handle_incoming(
|
||||
%{
|
||||
"type" => "Undo",
|
||||
"object" => %{"type" => "Announce", "object" => object_id},
|
||||
"actor" => actor,
|
||||
"id" => id
|
||||
} = data
|
||||
) do
|
||||
with %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
|
||||
{:ok, object} <-
|
||||
get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id),
|
||||
{:ok, activity, _, _} <- ActivityPub.unannounce(actor, object, id, false) do
|
||||
{:ok, activity}
|
||||
else
|
||||
e -> :error
|
||||
end
|
||||
end
|
||||
|
||||
def handle_incoming(
|
||||
%{
|
||||
"type" => "Undo",
|
||||
"object" => %{"type" => "Like", "object" => object_id},
|
||||
"actor" => actor,
|
||||
"id" => id
|
||||
} = data
|
||||
) do
|
||||
with %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
|
||||
{:ok, object} <-
|
||||
get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id),
|
||||
{:ok, activity, _, _} <- ActivityPub.unlike(actor, object, id, false) do
|
||||
{:ok, activity}
|
||||
else
|
||||
e -> :error
|
||||
end
|
||||
end
|
||||
|
||||
# TODO
|
||||
# Accept
|
||||
# Undo
|
||||
# Undo for non-Announce
|
||||
|
||||
def handle_incoming(_), do: :error
|
||||
|
||||
|
|
@ -477,4 +513,17 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
Repo.delete_all(q)
|
||||
end
|
||||
end
|
||||
|
||||
def maybe_fix_user_url(data) do
|
||||
if is_map(data["url"]) do
|
||||
data = Map.put(data, "url", data["url"]["href"])
|
||||
end
|
||||
|
||||
data
|
||||
end
|
||||
|
||||
def maybe_fix_user_object(data) do
|
||||
data
|
||||
|> maybe_fix_user_url
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,6 +5,22 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
alias Ecto.{Changeset, UUID}
|
||||
import Ecto.Query
|
||||
|
||||
# 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 normalize_actor(actor) do
|
||||
cond do
|
||||
is_binary(actor) ->
|
||||
actor
|
||||
|
||||
is_map(actor) ->
|
||||
actor["id"]
|
||||
end
|
||||
end
|
||||
|
||||
def normalize_params(params) do
|
||||
Map.put(params, "actor", normalize_actor(params["actor"]))
|
||||
end
|
||||
|
||||
def make_json_ld_header do
|
||||
%{
|
||||
"@context" => [
|
||||
|
|
@ -237,6 +253,28 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
|
||||
#### Announce-related helpers
|
||||
|
||||
@doc """
|
||||
Retruns an existing announce activity if the notice has already been announced
|
||||
"""
|
||||
def get_existing_announce(actor, %{data: %{"id" => id}}) do
|
||||
query =
|
||||
from(
|
||||
activity in Activity,
|
||||
where: fragment("(?)->>'actor' = ?", activity.data, ^actor),
|
||||
# this is to use the index
|
||||
where:
|
||||
fragment(
|
||||
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
||||
activity.data,
|
||||
activity.data,
|
||||
^id
|
||||
),
|
||||
where: fragment("(?)->>'type' = 'Announce'", activity.data)
|
||||
)
|
||||
|
||||
Repo.one(query)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Make announce activity data for the given actor and object
|
||||
"""
|
||||
|
|
@ -257,12 +295,55 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
||||
end
|
||||
|
||||
@doc """
|
||||
Make unannounce activity data for the given actor and object
|
||||
"""
|
||||
def make_unannounce_data(
|
||||
%User{ap_id: ap_id} = user,
|
||||
%Activity{data: %{"context" => context}} = activity,
|
||||
activity_id
|
||||
) do
|
||||
data = %{
|
||||
"type" => "Undo",
|
||||
"actor" => ap_id,
|
||||
"object" => activity.data,
|
||||
"to" => [user.follower_address, activity.data["actor"]],
|
||||
"cc" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"context" => context
|
||||
}
|
||||
|
||||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
||||
end
|
||||
|
||||
def make_unlike_data(
|
||||
%User{ap_id: ap_id} = user,
|
||||
%Activity{data: %{"context" => context}} = activity,
|
||||
activity_id
|
||||
) do
|
||||
data = %{
|
||||
"type" => "Undo",
|
||||
"actor" => ap_id,
|
||||
"object" => activity.data,
|
||||
"to" => [user.follower_address, activity.data["actor"]],
|
||||
"cc" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"context" => context
|
||||
}
|
||||
|
||||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
||||
end
|
||||
|
||||
def add_announce_to_object(%Activity{data: %{"actor" => actor}}, object) do
|
||||
with announcements <- [actor | object.data["announcements"] || []] |> Enum.uniq() do
|
||||
update_element_in_object("announcement", announcements, object)
|
||||
end
|
||||
end
|
||||
|
||||
def remove_announce_from_object(%Activity{data: %{"actor" => actor}}, object) do
|
||||
with announcements <- (object.data["announcements"] || []) |> List.delete(actor) do
|
||||
update_element_in_object("announcement", announcements, object)
|
||||
end
|
||||
end
|
||||
|
||||
#### Unfollow-related helpers
|
||||
|
||||
def make_unfollow_data(follower, followed, follow_activity) do
|
||||
|
|
|
|||
|
|
@ -24,6 +24,16 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
end
|
||||
end
|
||||
|
||||
def unrepeat(id_or_ap_id, user) do
|
||||
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||
object <- Object.get_by_ap_id(activity.data["object"]["id"]) do
|
||||
ActivityPub.unannounce(user, object)
|
||||
else
|
||||
_ ->
|
||||
{:error, "Could not unrepeat"}
|
||||
end
|
||||
end
|
||||
|
||||
def favorite(id_or_ap_id, user) do
|
||||
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||
false <- activity.data["actor"] == user.ap_id,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
defmodule Pleroma.Web.CommonAPI.Utils do
|
||||
alias Pleroma.{Repo, Object, Formatter, Activity}
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.User
|
||||
alias Calendar.Strftime
|
||||
alias Comeonin.Pbkdf2
|
||||
|
||||
# This is a hack for twidere.
|
||||
def get_by_id_or_ap_id(id) do
|
||||
|
|
@ -184,4 +186,13 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
String.slice(name, 0..30) <> "…"
|
||||
end
|
||||
end
|
||||
|
||||
def confirm_current_password(user, params) do
|
||||
with %User{local: true} = db_user <- Repo.get(User, user.id),
|
||||
true <- Pbkdf2.checkpw(params["password"], db_user.password_hash) do
|
||||
{:ok, db_user}
|
||||
else
|
||||
_ -> {:error, "Invalid password."}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ defmodule Pleroma.Web.Federator do
|
|||
alias Pleroma.Web.{WebFinger, Websub}
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
require Logger
|
||||
|
||||
@websub Application.get_env(:pleroma, :websub)
|
||||
|
|
@ -91,6 +92,8 @@ defmodule Pleroma.Web.Federator do
|
|||
def handle(:incoming_ap_doc, params) do
|
||||
Logger.info("Handling incoming AP activity")
|
||||
|
||||
params = Utils.normalize_params(params)
|
||||
|
||||
with {:ok, _user} <- ap_enabled_actor(params["actor"]),
|
||||
nil <- Activity.get_by_ap_id(params["id"]),
|
||||
{:ok, _activity} <- Transmogrifier.handle_incoming(params) do
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# https://tools.ietf.org/html/draft-cavage-http-signatures-08
|
||||
defmodule Pleroma.Web.HTTPSignatures do
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
require Logger
|
||||
|
||||
|
|
@ -31,14 +32,14 @@ defmodule Pleroma.Web.HTTPSignatures do
|
|||
def validate_conn(conn) do
|
||||
# TODO: How to get the right key and see if it is actually valid for that request.
|
||||
# For now, fetch the key for the actor.
|
||||
with actor_id <- conn.params["actor"],
|
||||
with actor_id <- Utils.normalize_actor(conn.params["actor"]),
|
||||
{:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do
|
||||
if validate_conn(conn, public_key) do
|
||||
true
|
||||
else
|
||||
Logger.debug("Could not validate, re-fetching user and trying one more time")
|
||||
# Fetch user anew and try one more time
|
||||
with actor_id <- conn.params["actor"],
|
||||
with actor_id <- Utils.normalize_actor(conn.params["actor"]),
|
||||
{:ok, _user} <- ActivityPub.make_user_from_ap_id(actor_id),
|
||||
{:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do
|
||||
validate_conn(conn, public_key)
|
||||
|
|
|
|||
|
|
@ -204,21 +204,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
|> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity})
|
||||
end
|
||||
|
||||
def user_statuses(%{assigns: %{user: user}} = conn, params) do
|
||||
with %User{ap_id: ap_id} <- Repo.get(User, params["id"]) do
|
||||
params =
|
||||
params
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("actor_id", ap_id)
|
||||
|> Map.put("whole_db", true)
|
||||
|
||||
def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
||||
with %User{} = user <- Repo.get(User, params["id"]) do
|
||||
# Since Pleroma has no "pinned" posts feature, we'll just set an empty list here
|
||||
activities =
|
||||
if params["pinned"] == "true" do
|
||||
# Since Pleroma has no "pinned" posts feature, we'll just set an empty list here
|
||||
[]
|
||||
else
|
||||
ActivityPub.fetch_public_activities(params)
|
||||
|> Enum.reverse()
|
||||
ActivityPub.fetch_user_activities(user, reading_user, params)
|
||||
end
|
||||
|
||||
conn
|
||||
|
|
@ -317,6 +310,13 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def unreblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||
with {:ok, _, _, %{data: %{"id" => id}}} = CommonAPI.unrepeat(ap_id_or_id, user),
|
||||
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
||||
render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
|
||||
end
|
||||
end
|
||||
|
||||
def fav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||
with {:ok, _fav, %{data: %{"id" => id}}} = CommonAPI.favorite(ap_id_or_id, user),
|
||||
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
||||
|
|
@ -325,7 +325,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
|
||||
def unfav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||
with {:ok, %{data: %{"id" => id}}} = CommonAPI.unfavorite(ap_id_or_id, user),
|
||||
with {:ok, _, _, %{data: %{"id" => id}}} = CommonAPI.unfavorite(ap_id_or_id, user),
|
||||
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
||||
render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
|
||||
end
|
||||
|
|
|
|||
|
|
@ -239,27 +239,35 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
|
|||
inserted_at = activity.data["published"]
|
||||
|
||||
author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
|
||||
follow_activity = Activity.get_by_ap_id(activity.data["object"])
|
||||
|
||||
follow_activity =
|
||||
if is_map(activity.data["object"]) do
|
||||
Activity.get_by_ap_id(activity.data["object"]["id"])
|
||||
else
|
||||
Activity.get_by_ap_id(activity.data["object"])
|
||||
end
|
||||
|
||||
mentions = (activity.recipients || []) |> get_mentions
|
||||
|
||||
[
|
||||
{:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']},
|
||||
{:"activity:verb", ['http://activitystrea.ms/schema/1.0/unfollow']},
|
||||
{:id, h.(activity.data["id"])},
|
||||
{:title, ['#{user.nickname} stopped following #{follow_activity.data["object"]}']},
|
||||
{:content, [type: 'html'],
|
||||
['#{user.nickname} stopped following #{follow_activity.data["object"]}']},
|
||||
{:published, h.(inserted_at)},
|
||||
{:updated, h.(updated_at)},
|
||||
{:"activity:object",
|
||||
[
|
||||
{:"activity:object-type", ['http://activitystrea.ms/schema/1.0/person']},
|
||||
{:id, h.(follow_activity.data["object"])},
|
||||
{:uri, h.(follow_activity.data["object"])}
|
||||
]},
|
||||
{:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []}
|
||||
] ++ mentions ++ author
|
||||
if follow_activity do
|
||||
[
|
||||
{:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']},
|
||||
{:"activity:verb", ['http://activitystrea.ms/schema/1.0/unfollow']},
|
||||
{:id, h.(activity.data["id"])},
|
||||
{:title, ['#{user.nickname} stopped following #{follow_activity.data["object"]}']},
|
||||
{:content, [type: 'html'],
|
||||
['#{user.nickname} stopped following #{follow_activity.data["object"]}']},
|
||||
{:published, h.(inserted_at)},
|
||||
{:updated, h.(updated_at)},
|
||||
{:"activity:object",
|
||||
[
|
||||
{:"activity:object-type", ['http://activitystrea.ms/schema/1.0/person']},
|
||||
{:id, h.(follow_activity.data["object"])},
|
||||
{:uri, h.(follow_activity.data["object"])}
|
||||
]},
|
||||
{:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []}
|
||||
] ++ mentions ++ author
|
||||
end
|
||||
end
|
||||
|
||||
def to_simple_form(%{data: %{"type" => "Delete"}} = activity, user, with_author) do
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ defmodule Pleroma.Web.Router do
|
|||
scope "/api/pleroma", Pleroma.Web.TwitterAPI do
|
||||
pipe_through(:authenticated_api)
|
||||
post("/follow_import", UtilController, :follow_import)
|
||||
post("/delete_account", UtilController, :delete_account)
|
||||
end
|
||||
|
||||
scope "/oauth", Pleroma.Web.OAuth do
|
||||
|
|
@ -114,6 +115,7 @@ defmodule Pleroma.Web.Router do
|
|||
delete("/statuses/:id", MastodonAPIController, :delete_status)
|
||||
|
||||
post("/statuses/:id/reblog", MastodonAPIController, :reblog_status)
|
||||
post("/statuses/:id/unreblog", MastodonAPIController, :unreblog_status)
|
||||
post("/statuses/:id/favourite", MastodonAPIController, :fav_status)
|
||||
post("/statuses/:id/unfavourite", MastodonAPIController, :unfav_status)
|
||||
|
||||
|
|
|
|||
|
|
@ -187,13 +187,14 @@ defmodule Pleroma.Web.Salmon do
|
|||
|
||||
def publish(%{info: %{"keys" => keys}} = user, %{data: %{"type" => type}} = activity, poster)
|
||||
when type in @supported_activities do
|
||||
feed =
|
||||
ActivityRepresenter.to_simple_form(activity, user, true)
|
||||
|> ActivityRepresenter.wrap_with_entry()
|
||||
|> :xmerl.export_simple(:xmerl_xml)
|
||||
|> to_string
|
||||
feed = ActivityRepresenter.to_simple_form(activity, user, true)
|
||||
|
||||
if feed do
|
||||
feed =
|
||||
ActivityRepresenter.wrap_with_entry(feed)
|
||||
|> :xmerl.export_simple(:xmerl_xml)
|
||||
|> to_string
|
||||
|
||||
{:ok, private, _} = keys_from_pem(keys)
|
||||
{:ok, feed} = encode(private, feed)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
alias Pleroma.Web
|
||||
alias Pleroma.Web.OStatus
|
||||
alias Pleroma.Web.WebFinger
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Comeonin.Pbkdf2
|
||||
alias Pleroma.Formatter
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
|
|
@ -195,4 +196,15 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
|
||||
json(conn, "job started")
|
||||
end
|
||||
|
||||
def delete_account(%{assigns: %{user: user}} = conn, params) do
|
||||
case CommonAPI.Utils.confirm_current_password(user, params) do
|
||||
{:ok, user} ->
|
||||
Task.start(fn -> User.delete(user) end)
|
||||
json(conn, %{status: "success"})
|
||||
|
||||
{:error, msg} ->
|
||||
json(conn, %{error: msg})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -197,7 +197,8 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
|
|||
"external_url" => object["external_url"] || object["id"],
|
||||
"tags" => tags,
|
||||
"activity_type" => "post",
|
||||
"possibly_sensitive" => possibly_sensitive
|
||||
"possibly_sensitive" => possibly_sensitive,
|
||||
"visibility" => Pleroma.Web.MastodonAPI.StatusView.get_visibility(object)
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,18 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||
CommonAPI.post(user, data)
|
||||
end
|
||||
|
||||
def delete(%User{} = user, id) do
|
||||
# TwitterAPI does not have an "unretweet" endpoint; instead this is done
|
||||
# via the "destroy" endpoint. Therefore, we need to handle
|
||||
# when the status to "delete" is actually an Announce (repeat) object.
|
||||
with %Activity{data: %{"type" => type}} <- Repo.get(Activity, id) do
|
||||
case type do
|
||||
"Announce" -> unrepeat(user, id)
|
||||
_ -> CommonAPI.delete(id, user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def follow(%User{} = follower, params) do
|
||||
with {:ok, %User{} = followed} <- get_user(params),
|
||||
{:ok, follower} <- User.follow(follower, followed),
|
||||
|
|
@ -63,15 +75,21 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||
end
|
||||
end
|
||||
|
||||
defp unrepeat(%User{} = user, ap_id_or_id) do
|
||||
with {:ok, _unannounce, activity, _object} <- CommonAPI.unrepeat(ap_id_or_id, user) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
||||
def fav(%User{} = user, ap_id_or_id) do
|
||||
with {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.favorite(ap_id_or_id, user),
|
||||
with {:ok, _fav, %{data: %{"id" => id}}} = CommonAPI.favorite(ap_id_or_id, user),
|
||||
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
||||
def unfav(%User{} = user, ap_id_or_id) do
|
||||
with {:ok, %{data: %{"id" => id}}} = CommonAPI.unfavorite(ap_id_or_id, user),
|
||||
with {:ok, _unfav, _fav, %{data: %{"id" => id}}} = CommonAPI.unfavorite(ap_id_or_id, user),
|
||||
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
||||
{:ok, activity}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -96,13 +96,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
def user_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||
case TwitterAPI.get_user(user, params) do
|
||||
{:ok, target_user} ->
|
||||
params =
|
||||
params
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("actor_id", target_user.ap_id)
|
||||
|> Map.put("whole_db", true)
|
||||
|
||||
activities = ActivityPub.fetch_public_activities(params)
|
||||
activities = ActivityPub.fetch_user_activities(target_user, user, params)
|
||||
|
||||
conn
|
||||
|> render(ActivityView, "index.json", %{activities: activities, for: user})
|
||||
|
|
@ -157,8 +151,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
end
|
||||
|
||||
def delete_post(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with {:ok, delete} <- CommonAPI.delete(id, user) do
|
||||
render(conn, ActivityView, "activity.json", %{activity: delete, for: user})
|
||||
with {:ok, activity} <- TwitterAPI.delete(user, id) do
|
||||
render(conn, ActivityView, "activity.json", %{activity: activity, for: user})
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -262,7 +262,8 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
|
|||
"external_url" => object["external_url"] || object["id"],
|
||||
"tags" => tags,
|
||||
"activity_type" => "post",
|
||||
"possibly_sensitive" => possibly_sensitive
|
||||
"possibly_sensitive" => possibly_sensitive,
|
||||
"visibility" => Pleroma.Web.MastodonAPI.StatusView.get_visibility(object)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -86,6 +86,11 @@ defmodule Pleroma.Web.WebFinger do
|
|||
"href" => "data:application/magic-public-key,#{magic_key}"
|
||||
},
|
||||
%{"rel" => "self", "type" => "application/activity+json", "href" => user.ap_id},
|
||||
%{
|
||||
"rel" => "self",
|
||||
"type" => "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"",
|
||||
"href" => user.ap_id
|
||||
},
|
||||
%{
|
||||
"rel" => "http://ostatus.org/schema/1.0/subscribe",
|
||||
"template" => OStatus.remote_follow_path()
|
||||
|
|
@ -183,6 +188,9 @@ defmodule Pleroma.Web.WebFinger do
|
|||
{"application/activity+json", "self"} ->
|
||||
Map.put(data, "ap_id", link["href"])
|
||||
|
||||
{"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "self"} ->
|
||||
Map.put(data, "ap_id", link["href"])
|
||||
|
||||
{_, "magic-public-key"} ->
|
||||
"data:application/magic-public-key," <> magic_key = link["href"]
|
||||
Map.put(data, "magic_key", magic_key)
|
||||
|
|
@ -206,7 +214,7 @@ defmodule Pleroma.Web.WebFinger do
|
|||
end
|
||||
|
||||
def get_template_from_xml(body) do
|
||||
xpath = "//Link[@rel='lrdd' and @type='application/xrd+xml']/@template"
|
||||
xpath = "//Link[@rel='lrdd']/@template"
|
||||
|
||||
with doc when doc != :error <- XML.parse_document(body),
|
||||
template when template != nil <- XML.string_from_xpath(xpath, doc) do
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue