Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into rum-index
This commit is contained in:
commit
412a3d8a0f
173 changed files with 4238 additions and 1179 deletions
|
|
@ -5,7 +5,6 @@
|
|||
defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Conversation
|
||||
alias Pleroma.Instances
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Object.Fetcher
|
||||
|
|
@ -15,7 +14,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.Federator
|
||||
alias Pleroma.Web.WebFinger
|
||||
|
||||
import Ecto.Query
|
||||
|
|
@ -24,8 +22,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
require Logger
|
||||
|
||||
@httpoison Application.get_env(:pleroma, :httpoison)
|
||||
|
||||
# For Announce activities, we filter the recipients based on following status for any actors
|
||||
# that match actual users. See issue #164 for more information about why this is necessary.
|
||||
defp get_recipients(%{"type" => "Announce"} = data) do
|
||||
|
|
@ -137,9 +133,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
activity
|
||||
end
|
||||
|
||||
Task.start(fn ->
|
||||
Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
|
||||
end)
|
||||
PleromaJobQueue.enqueue(:background, Pleroma.Web.RichMedia.Helpers, [:fetch, activity])
|
||||
|
||||
Notification.create_notifications(activity)
|
||||
|
||||
|
|
@ -546,8 +540,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
)
|
||||
)
|
||||
|
||||
Ecto.Adapters.SQL.to_sql(:all, Repo, query)
|
||||
|
||||
query
|
||||
else
|
||||
Logger.error("Could not restrict visibility to #{visibility}")
|
||||
|
|
@ -563,8 +555,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
|
||||
)
|
||||
|
||||
Ecto.Adapters.SQL.to_sql(:all, Repo, query)
|
||||
|
||||
query
|
||||
end
|
||||
|
||||
|
|
@ -575,6 +565,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
defp restrict_visibility(query, _visibility), do: query
|
||||
|
||||
defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}) do
|
||||
query =
|
||||
from(
|
||||
a in query,
|
||||
where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
|
||||
)
|
||||
|
||||
query
|
||||
end
|
||||
|
||||
defp restrict_thread_visibility(query, _), do: query
|
||||
|
||||
def fetch_user_activities(user, reading_user, params \\ %{}) do
|
||||
params =
|
||||
params
|
||||
|
|
@ -701,6 +703,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
defp restrict_type(query, _), do: query
|
||||
|
||||
defp restrict_state(query, %{"state" => state}) do
|
||||
from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
|
||||
end
|
||||
|
||||
defp restrict_state(query, _), do: query
|
||||
|
||||
defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
|
||||
from(
|
||||
activity in query,
|
||||
|
|
@ -756,8 +764,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
blocks = info.blocks || []
|
||||
domain_blocks = info.domain_blocks || []
|
||||
|
||||
query =
|
||||
if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
|
||||
|
||||
from(
|
||||
activity in query,
|
||||
[activity, object: o] in query,
|
||||
where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
|
||||
where: fragment("not (? && ?)", activity.recipients, ^blocks),
|
||||
where:
|
||||
|
|
@ -767,7 +778,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
activity.data,
|
||||
^blocks
|
||||
),
|
||||
where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks)
|
||||
where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks),
|
||||
where: fragment("not (split_part(?->>'actor', '/', 3) = ANY(?))", o.data, ^domain_blocks)
|
||||
)
|
||||
end
|
||||
|
||||
|
|
@ -849,15 +861,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|> restrict_local(opts)
|
||||
|> restrict_actor(opts)
|
||||
|> restrict_type(opts)
|
||||
|> restrict_state(opts)
|
||||
|> restrict_favorited_by(opts)
|
||||
|> restrict_blocked(opts)
|
||||
|> restrict_muted(opts)
|
||||
|> restrict_media(opts)
|
||||
|> restrict_visibility(opts)
|
||||
|> restrict_thread_visibility(opts)
|
||||
|> restrict_replies(opts)
|
||||
|> restrict_reblogs(opts)
|
||||
|> restrict_pinned(opts)
|
||||
|> restrict_muted_reblogs(opts)
|
||||
|> Activity.restrict_deactivated_users()
|
||||
end
|
||||
|
||||
def fetch_activities(recipients, opts \\ %{}) do
|
||||
|
|
@ -961,89 +976,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
end
|
||||
end
|
||||
|
||||
def should_federate?(inbox, public) do
|
||||
if public do
|
||||
true
|
||||
else
|
||||
inbox_info = URI.parse(inbox)
|
||||
!Enum.member?(Pleroma.Config.get([:instance, :quarantined_instances], []), inbox_info.host)
|
||||
end
|
||||
end
|
||||
|
||||
def publish(actor, activity) do
|
||||
remote_followers =
|
||||
if actor.follower_address in activity.recipients do
|
||||
{:ok, followers} = User.get_followers(actor)
|
||||
followers |> Enum.filter(&(!&1.local))
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
public = is_public?(activity)
|
||||
|
||||
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
json = Jason.encode!(data)
|
||||
|
||||
(Pleroma.Web.Salmon.remote_users(activity) ++ remote_followers)
|
||||
|> Enum.filter(fn user -> User.ap_enabled?(user) end)
|
||||
|> Enum.map(fn %{info: %{source_data: data}} ->
|
||||
(is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"]
|
||||
end)
|
||||
|> Enum.uniq()
|
||||
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|
||||
|> Instances.filter_reachable()
|
||||
|> Enum.each(fn {inbox, unreachable_since} ->
|
||||
Federator.publish_single_ap(%{
|
||||
inbox: inbox,
|
||||
json: json,
|
||||
actor: actor,
|
||||
id: activity.data["id"],
|
||||
unreachable_since: unreachable_since
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
def publish_one(%{inbox: inbox, json: json, actor: actor, id: id} = params) do
|
||||
Logger.info("Federating #{id} to #{inbox}")
|
||||
host = URI.parse(inbox).host
|
||||
|
||||
digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64())
|
||||
|
||||
date =
|
||||
NaiveDateTime.utc_now()
|
||||
|> Timex.format!("{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT")
|
||||
|
||||
signature =
|
||||
Pleroma.Web.HTTPSignatures.sign(actor, %{
|
||||
host: host,
|
||||
"content-length": byte_size(json),
|
||||
digest: digest,
|
||||
date: date
|
||||
})
|
||||
|
||||
with {:ok, %{status: code}} when code in 200..299 <-
|
||||
result =
|
||||
@httpoison.post(
|
||||
inbox,
|
||||
json,
|
||||
[
|
||||
{"Content-Type", "application/activity+json"},
|
||||
{"Date", date},
|
||||
{"signature", signature},
|
||||
{"digest", digest}
|
||||
]
|
||||
) do
|
||||
if !Map.has_key?(params, :unreachable_since) || params[:unreachable_since],
|
||||
do: Instances.set_reachable(inbox)
|
||||
|
||||
result
|
||||
else
|
||||
{_post_result, response} ->
|
||||
unless params[:unreachable_since], do: Instances.set_unreachable(inbox)
|
||||
{:error, response}
|
||||
end
|
||||
end
|
||||
|
||||
# filter out broken threads
|
||||
def contain_broken_threads(%Activity{} = activity, %User{} = user) do
|
||||
entire_thread_visible_for_user?(activity, user)
|
||||
|
|
@ -1054,11 +986,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
contain_broken_threads(activity, user)
|
||||
end
|
||||
|
||||
# do post-processing on a timeline
|
||||
def contain_timeline(timeline, user) do
|
||||
timeline
|
||||
|> Enum.filter(fn activity ->
|
||||
contain_activity(activity, user)
|
||||
end)
|
||||
def fetch_direct_messages_query do
|
||||
Activity
|
||||
|> restrict_type(%{"type" => "Create"})
|
||||
|> restrict_visibility(%{visibility: "direct"})
|
||||
|> order_by([activity], asc: activity.id)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
|||
object =
|
||||
if Enum.member?(Pleroma.Config.get([:mrf_simple, :media_nsfw]), actor_host) do
|
||||
tags = (child_object["tag"] || []) ++ ["nsfw"]
|
||||
child_object = Map.put(child_object, "tags", tags)
|
||||
child_object = Map.put(child_object, "tag", tags)
|
||||
child_object = Map.put(child_object, "sensitive", true)
|
||||
Map.put(object, "object", child_object)
|
||||
else
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
|
|||
|
||||
object =
|
||||
object
|
||||
|> Map.put("tags", tags)
|
||||
|> Map.put("tag", tags)
|
||||
|> Map.put("sensitive", true)
|
||||
|
||||
message = Map.put(message, "object", object)
|
||||
|
|
|
|||
152
lib/pleroma/web/activity_pub/publisher.ex
Normal file
152
lib/pleroma/web/activity_pub/publisher.ex
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.Publisher do
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Instances
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Relay
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
|
||||
import Pleroma.Web.ActivityPub.Visibility
|
||||
|
||||
@behaviour Pleroma.Web.Federator.Publisher
|
||||
|
||||
require Logger
|
||||
|
||||
@httpoison Application.get_env(:pleroma, :httpoison)
|
||||
|
||||
@moduledoc """
|
||||
ActivityPub outgoing federation module.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Determine if an activity can be represented by running it through Transmogrifier.
|
||||
"""
|
||||
def is_representable?(%Activity{} = activity) do
|
||||
with {:ok, _data} <- Transmogrifier.prepare_outgoing(activity.data) do
|
||||
true
|
||||
else
|
||||
_e ->
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Publish a single message to a peer. Takes a struct with the following
|
||||
parameters set:
|
||||
|
||||
* `inbox`: the inbox to publish to
|
||||
* `json`: the JSON message body representing the ActivityPub message
|
||||
* `actor`: the actor which is signing the message
|
||||
* `id`: the ActivityStreams URI of the message
|
||||
"""
|
||||
def publish_one(%{inbox: inbox, json: json, actor: %User{} = actor, id: id} = params) do
|
||||
Logger.info("Federating #{id} to #{inbox}")
|
||||
host = URI.parse(inbox).host
|
||||
|
||||
digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64())
|
||||
|
||||
date =
|
||||
NaiveDateTime.utc_now()
|
||||
|> Timex.format!("{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT")
|
||||
|
||||
signature =
|
||||
Pleroma.Signature.sign(actor, %{
|
||||
host: host,
|
||||
"content-length": byte_size(json),
|
||||
digest: digest,
|
||||
date: date
|
||||
})
|
||||
|
||||
with {:ok, %{status: code}} when code in 200..299 <-
|
||||
result =
|
||||
@httpoison.post(
|
||||
inbox,
|
||||
json,
|
||||
[
|
||||
{"Content-Type", "application/activity+json"},
|
||||
{"Date", date},
|
||||
{"signature", signature},
|
||||
{"digest", digest}
|
||||
]
|
||||
) do
|
||||
if !Map.has_key?(params, :unreachable_since) || params[:unreachable_since],
|
||||
do: Instances.set_reachable(inbox)
|
||||
|
||||
result
|
||||
else
|
||||
{_post_result, response} ->
|
||||
unless params[:unreachable_since], do: Instances.set_unreachable(inbox)
|
||||
{:error, response}
|
||||
end
|
||||
end
|
||||
|
||||
defp should_federate?(inbox, public) do
|
||||
if public do
|
||||
true
|
||||
else
|
||||
inbox_info = URI.parse(inbox)
|
||||
!Enum.member?(Pleroma.Config.get([:instance, :quarantined_instances], []), inbox_info.host)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Publishes an activity to all relevant peers.
|
||||
"""
|
||||
def publish(%User{} = actor, %Activity{} = activity) do
|
||||
remote_followers =
|
||||
if actor.follower_address in activity.recipients do
|
||||
{:ok, followers} = User.get_followers(actor)
|
||||
followers |> Enum.filter(&(!&1.local))
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
public = is_public?(activity)
|
||||
|
||||
if public && Config.get([:instance, :allow_relay]) do
|
||||
Logger.info(fn -> "Relaying #{activity.data["id"]} out" end)
|
||||
Relay.publish(activity)
|
||||
end
|
||||
|
||||
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
json = Jason.encode!(data)
|
||||
|
||||
(Pleroma.Web.Salmon.remote_users(activity) ++ remote_followers)
|
||||
|> Enum.filter(fn user -> User.ap_enabled?(user) end)
|
||||
|> Enum.map(fn %{info: %{source_data: data}} ->
|
||||
(is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"]
|
||||
end)
|
||||
|> Enum.uniq()
|
||||
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|
||||
|> Instances.filter_reachable()
|
||||
|> Enum.each(fn {inbox, unreachable_since} ->
|
||||
Pleroma.Web.Federator.Publisher.enqueue_one(
|
||||
__MODULE__,
|
||||
%{
|
||||
inbox: inbox,
|
||||
json: json,
|
||||
actor: actor,
|
||||
id: activity.data["id"],
|
||||
unreachable_since: unreachable_since
|
||||
}
|
||||
)
|
||||
end)
|
||||
end
|
||||
|
||||
def gather_webfinger_links(%User{} = user) do
|
||||
[
|
||||
%{"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
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
def gather_nodeinfo_protocol_names, do: ["activitypub"]
|
||||
end
|
||||
|
|
@ -11,7 +11,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
alias Pleroma.Object.Containment
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
require Logger
|
||||
|
||||
@supported_object_types ["Article", "Note", "Video", "Page"]
|
||||
@supported_report_states ~w(open closed resolved)
|
||||
@valid_visibilities ~w(public unlisted private direct)
|
||||
|
||||
# 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.
|
||||
|
|
@ -670,7 +672,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
"actor" => params.actor.ap_id,
|
||||
"content" => params.content,
|
||||
"object" => object,
|
||||
"context" => params.context
|
||||
"context" => params.context,
|
||||
"state" => "open"
|
||||
}
|
||||
|> Map.merge(additional)
|
||||
end
|
||||
|
|
@ -682,7 +685,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
"""
|
||||
def fetch_ordered_collection(from, pages_left, acc \\ []) do
|
||||
with {:ok, response} <- Tesla.get(from),
|
||||
{:ok, collection} <- Poison.decode(response.body) do
|
||||
{:ok, collection} <- Jason.decode(response.body) do
|
||||
case collection["type"] do
|
||||
"OrderedCollection" ->
|
||||
# If we've encountered the OrderedCollection and not the page,
|
||||
|
|
@ -713,4 +716,77 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
#### Report-related helpers
|
||||
|
||||
def update_report_state(%Activity{} = activity, state) when state in @supported_report_states do
|
||||
with new_data <- Map.put(activity.data, "state", state),
|
||||
changeset <- Changeset.change(activity, data: new_data),
|
||||
{:ok, activity} <- Repo.update(changeset) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
||||
def update_report_state(_, _), do: {:error, "Unsupported state"}
|
||||
|
||||
def update_activity_visibility(activity, visibility) when visibility in @valid_visibilities do
|
||||
[to, cc, recipients] =
|
||||
activity
|
||||
|> get_updated_targets(visibility)
|
||||
|> Enum.map(&Enum.uniq/1)
|
||||
|
||||
object_data =
|
||||
activity.object.data
|
||||
|> Map.put("to", to)
|
||||
|> Map.put("cc", cc)
|
||||
|
||||
{:ok, object} =
|
||||
activity.object
|
||||
|> Object.change(%{data: object_data})
|
||||
|> Object.update_and_set_cache()
|
||||
|
||||
activity_data =
|
||||
activity.data
|
||||
|> Map.put("to", to)
|
||||
|> Map.put("cc", cc)
|
||||
|
||||
activity
|
||||
|> Map.put(:object, object)
|
||||
|> Activity.change(%{data: activity_data, recipients: recipients})
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def update_activity_visibility(_, _), do: {:error, "Unsupported visibility"}
|
||||
|
||||
defp get_updated_targets(
|
||||
%Activity{data: %{"to" => to} = data, recipients: recipients},
|
||||
visibility
|
||||
) do
|
||||
cc = Map.get(data, "cc", [])
|
||||
follower_address = User.get_cached_by_ap_id(data["actor"]).follower_address
|
||||
public = "https://www.w3.org/ns/activitystreams#Public"
|
||||
|
||||
case visibility do
|
||||
"public" ->
|
||||
to = [public | List.delete(to, follower_address)]
|
||||
cc = [follower_address | List.delete(cc, public)]
|
||||
recipients = [public | recipients]
|
||||
[to, cc, recipients]
|
||||
|
||||
"private" ->
|
||||
to = [follower_address | List.delete(to, public)]
|
||||
cc = List.delete(cc, public)
|
||||
recipients = List.delete(recipients, public)
|
||||
[to, cc, recipients]
|
||||
|
||||
"unlisted" ->
|
||||
to = [follower_address | List.delete(to, public)]
|
||||
cc = [public | List.delete(cc, follower_address)]
|
||||
recipients = recipients ++ [follower_address, public]
|
||||
[to, cc, recipients]
|
||||
|
||||
_ ->
|
||||
[to, cc, recipients]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
defmodule Pleroma.Web.ActivityPub.Visibility do
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
|
||||
def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false
|
||||
|
|
@ -13,11 +14,12 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|
|||
end
|
||||
|
||||
def is_private?(activity) do
|
||||
unless is_public?(activity) do
|
||||
follower_address = User.get_cached_by_ap_id(activity.data["actor"]).follower_address
|
||||
Enum.any?(activity.data["to"], &(&1 == follower_address))
|
||||
with false <- is_public?(activity),
|
||||
%User{follower_address: follower_address} <-
|
||||
User.get_cached_by_ap_id(activity.data["actor"]) do
|
||||
follower_address in activity.data["to"]
|
||||
else
|
||||
false
|
||||
_ -> false
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -38,24 +40,37 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|
|||
visible_for_user?(activity, nil) || Enum.any?(x, &(&1 in y))
|
||||
end
|
||||
|
||||
# guard
|
||||
def entire_thread_visible_for_user?(nil, _user), do: false
|
||||
def entire_thread_visible_for_user?(%Activity{} = activity, %User{} = user) do
|
||||
{:ok, %{rows: [[result]]}} =
|
||||
Ecto.Adapters.SQL.query(Repo, "SELECT thread_visibility($1, $2)", [
|
||||
user.ap_id,
|
||||
activity.data["id"]
|
||||
])
|
||||
|
||||
# XXX: Probably even more inefficient than the previous implementation intended to be a placeholder untill https://git.pleroma.social/pleroma/pleroma/merge_requests/971 is in develop
|
||||
# credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
|
||||
result
|
||||
end
|
||||
|
||||
def entire_thread_visible_for_user?(
|
||||
%Activity{} = tail,
|
||||
# %Activity{data: %{"object" => %{"inReplyTo" => parent_id}}} = tail,
|
||||
user
|
||||
) do
|
||||
case Object.normalize(tail) do
|
||||
%{data: %{"inReplyTo" => parent_id}} when is_binary(parent_id) ->
|
||||
parent = Activity.get_in_reply_to_activity(tail)
|
||||
visible_for_user?(tail, user) && entire_thread_visible_for_user?(parent, user)
|
||||
def get_visibility(object) do
|
||||
public = "https://www.w3.org/ns/activitystreams#Public"
|
||||
to = object.data["to"] || []
|
||||
cc = object.data["cc"] || []
|
||||
|
||||
_ ->
|
||||
visible_for_user?(tail, user)
|
||||
cond do
|
||||
public in to ->
|
||||
"public"
|
||||
|
||||
public in cc ->
|
||||
"unlisted"
|
||||
|
||||
# this should use the sql for the object's activity
|
||||
Enum.any?(to, &String.contains?(&1, "/followers")) ->
|
||||
"private"
|
||||
|
||||
length(cc) > 0 ->
|
||||
"private"
|
||||
|
||||
true ->
|
||||
"direct"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,11 +4,16 @@
|
|||
|
||||
defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||
use Pleroma.Web, :controller
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserInviteToken
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Relay
|
||||
alias Pleroma.Web.AdminAPI.AccountView
|
||||
alias Pleroma.Web.AdminAPI.ReportView
|
||||
alias Pleroma.Web.AdminAPI.Search
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
|
||||
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||
|
||||
|
|
@ -59,7 +64,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
bio: "."
|
||||
}
|
||||
|
||||
changeset = User.register_changeset(%User{}, user_data, confirmed: true)
|
||||
changeset = User.register_changeset(%User{}, user_data, need_confirmation: false)
|
||||
{:ok, user} = User.register(changeset)
|
||||
|
||||
conn
|
||||
|
|
@ -287,12 +292,88 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
|> json(token.token)
|
||||
end
|
||||
|
||||
def list_reports(conn, params) do
|
||||
params =
|
||||
params
|
||||
|> Map.put("type", "Flag")
|
||||
|> Map.put("skip_preload", true)
|
||||
|
||||
reports =
|
||||
[]
|
||||
|> ActivityPub.fetch_activities(params)
|
||||
|> Enum.reverse()
|
||||
|
||||
conn
|
||||
|> put_view(ReportView)
|
||||
|> render("index.json", %{reports: reports})
|
||||
end
|
||||
|
||||
def report_show(conn, %{"id" => id}) do
|
||||
with %Activity{} = report <- Activity.get_by_id(id) do
|
||||
conn
|
||||
|> put_view(ReportView)
|
||||
|> render("show.json", %{report: report})
|
||||
else
|
||||
_ -> {:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
def report_update_state(conn, %{"id" => id, "state" => state}) do
|
||||
with {:ok, report} <- CommonAPI.update_report_state(id, state) do
|
||||
conn
|
||||
|> put_view(ReportView)
|
||||
|> render("show.json", %{report: report})
|
||||
end
|
||||
end
|
||||
|
||||
def report_respond(%{assigns: %{user: user}} = conn, %{"id" => id} = params) do
|
||||
with false <- is_nil(params["status"]),
|
||||
%Activity{} <- Activity.get_by_id(id) do
|
||||
params =
|
||||
params
|
||||
|> Map.put("in_reply_to_status_id", id)
|
||||
|> Map.put("visibility", "direct")
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, params)
|
||||
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> render("status.json", %{activity: activity})
|
||||
else
|
||||
true ->
|
||||
{:param_cast, nil}
|
||||
|
||||
nil ->
|
||||
{:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
def status_update(conn, %{"id" => id} = params) do
|
||||
with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> render("status.json", %{activity: activity})
|
||||
end
|
||||
end
|
||||
|
||||
def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
|
||||
json(conn, %{})
|
||||
end
|
||||
end
|
||||
|
||||
def errors(conn, {:error, :not_found}) do
|
||||
conn
|
||||
|> put_status(404)
|
||||
|> json("Not found")
|
||||
end
|
||||
|
||||
def errors(conn, {:error, reason}) do
|
||||
conn
|
||||
|> put_status(400)
|
||||
|> json(reason)
|
||||
end
|
||||
|
||||
def errors(conn, {:param_cast, _}) do
|
||||
conn
|
||||
|> put_status(400)
|
||||
|
|
|
|||
41
lib/pleroma/web/admin_api/views/report_view.ex
Normal file
41
lib/pleroma/web/admin_api/views/report_view.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.AdminAPI.ReportView do
|
||||
use Pleroma.Web, :view
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.CommonAPI.Utils
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
|
||||
def render("index.json", %{reports: reports}) do
|
||||
%{
|
||||
reports: render_many(reports, __MODULE__, "show.json", as: :report)
|
||||
}
|
||||
end
|
||||
|
||||
def render("show.json", %{report: report}) do
|
||||
user = User.get_cached_by_ap_id(report.data["actor"])
|
||||
created_at = Utils.to_masto_date(report.data["published"])
|
||||
|
||||
[account_ap_id | status_ap_ids] = report.data["object"]
|
||||
account = User.get_cached_by_ap_id(account_ap_id)
|
||||
|
||||
statuses =
|
||||
Enum.map(status_ap_ids, fn ap_id ->
|
||||
Activity.get_by_ap_id_with_object(ap_id)
|
||||
end)
|
||||
|
||||
%{
|
||||
id: report.id,
|
||||
account: AccountView.render("account.json", %{user: account}),
|
||||
actor: AccountView.render("account.json", %{user: user}),
|
||||
content: report.data["content"],
|
||||
created_at: created_at,
|
||||
statuses: StatusView.render("index.json", %{activities: statuses, as: :activity}),
|
||||
state: report.data["state"]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -74,7 +74,7 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
|
|||
password_confirmation: random_password
|
||||
},
|
||||
external: true,
|
||||
confirmed: true
|
||||
need_confirmation: false
|
||||
)
|
||||
|> Repo.insert(),
|
||||
{:ok, _} <-
|
||||
|
|
|
|||
|
|
@ -71,6 +71,9 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
{:ok, _} <- unpin(activity_id, user),
|
||||
{:ok, delete} <- ActivityPub.delete(object) do
|
||||
{:ok, delete}
|
||||
else
|
||||
_ ->
|
||||
{:error, "Could not delete"}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -116,32 +119,34 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
end
|
||||
end
|
||||
|
||||
def get_visibility(%{"visibility" => visibility})
|
||||
def get_visibility(%{"visibility" => visibility}, in_reply_to)
|
||||
when visibility in ~w{public unlisted private direct},
|
||||
do: visibility
|
||||
do: {visibility, get_replied_to_visibility(in_reply_to)}
|
||||
|
||||
def get_visibility(%{"in_reply_to_status_id" => status_id}) when not is_nil(status_id) do
|
||||
case get_replied_to_activity(status_id) do
|
||||
nil ->
|
||||
"public"
|
||||
def get_visibility(_, in_reply_to) when not is_nil(in_reply_to) do
|
||||
visibility = get_replied_to_visibility(in_reply_to)
|
||||
{visibility, visibility}
|
||||
end
|
||||
|
||||
in_reply_to ->
|
||||
# XXX: these heuristics should be moved out of MastodonAPI.
|
||||
with %Object{} = object <- Object.normalize(in_reply_to) do
|
||||
Pleroma.Web.MastodonAPI.StatusView.get_visibility(object)
|
||||
end
|
||||
def get_visibility(_, in_reply_to), do: {"public", get_replied_to_visibility(in_reply_to)}
|
||||
|
||||
def get_replied_to_visibility(nil), do: nil
|
||||
|
||||
def get_replied_to_visibility(activity) do
|
||||
with %Object{} = object <- Object.normalize(activity) do
|
||||
Pleroma.Web.ActivityPub.Visibility.get_visibility(object)
|
||||
end
|
||||
end
|
||||
|
||||
def get_visibility(_), do: "public"
|
||||
|
||||
def post(user, %{"status" => status} = data) do
|
||||
visibility = get_visibility(data)
|
||||
limit = Pleroma.Config.get([:instance, :limit])
|
||||
|
||||
with status <- String.trim(status),
|
||||
attachments <- attachments_from_ids(data),
|
||||
in_reply_to <- get_replied_to_activity(data["in_reply_to_status_id"]),
|
||||
{visibility, in_reply_to_visibility} <- get_visibility(data, in_reply_to),
|
||||
{_, false} <-
|
||||
{:private_to_public, in_reply_to_visibility == "direct" && visibility != "direct"},
|
||||
{content_html, mentions, tags} <-
|
||||
make_content_html(
|
||||
status,
|
||||
|
|
@ -185,6 +190,8 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
)
|
||||
|
||||
res
|
||||
else
|
||||
e -> {:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -193,7 +200,7 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
user =
|
||||
with emoji <- emoji_from_profile(user),
|
||||
source_data <- (user.info.source_data || %{}) |> Map.put("tag", emoji),
|
||||
info_cng <- Pleroma.User.Info.set_source_data(user.info, source_data),
|
||||
info_cng <- User.Info.set_source_data(user.info, source_data),
|
||||
change <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
|
||||
{:ok, user} <- User.update_and_set_cache(change) do
|
||||
user
|
||||
|
|
@ -226,7 +233,7 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||
true <- Enum.member?(object_to, "https://www.w3.org/ns/activitystreams#Public"),
|
||||
%{valid?: true} = info_changeset <-
|
||||
Pleroma.User.Info.add_pinnned_activity(user.info, activity),
|
||||
User.Info.add_pinnned_activity(user.info, activity),
|
||||
changeset <-
|
||||
Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_changeset),
|
||||
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
||||
|
|
@ -243,7 +250,7 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
def unpin(id_or_ap_id, user) do
|
||||
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||
%{valid?: true} = info_changeset <-
|
||||
Pleroma.User.Info.remove_pinnned_activity(user.info, activity),
|
||||
User.Info.remove_pinnned_activity(user.info, activity),
|
||||
changeset <-
|
||||
Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_changeset),
|
||||
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
||||
|
|
@ -311,6 +318,60 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
end
|
||||
end
|
||||
|
||||
def update_report_state(activity_id, state) do
|
||||
with %Activity{} = activity <- Activity.get_by_id(activity_id),
|
||||
{:ok, activity} <- Utils.update_report_state(activity, state) do
|
||||
{:ok, activity}
|
||||
else
|
||||
nil ->
|
||||
{:error, :not_found}
|
||||
|
||||
{:error, reason} ->
|
||||
{:error, reason}
|
||||
|
||||
_ ->
|
||||
{:error, "Could not update state"}
|
||||
end
|
||||
end
|
||||
|
||||
def update_activity_scope(activity_id, opts \\ %{}) do
|
||||
with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
|
||||
{:ok, activity} <- toggle_sensitive(activity, opts),
|
||||
{:ok, activity} <- set_visibility(activity, opts) do
|
||||
{:ok, activity}
|
||||
else
|
||||
nil ->
|
||||
{:error, :not_found}
|
||||
|
||||
{:error, reason} ->
|
||||
{:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
defp toggle_sensitive(activity, %{"sensitive" => sensitive}) when sensitive in ~w(true false) do
|
||||
toggle_sensitive(activity, %{"sensitive" => String.to_existing_atom(sensitive)})
|
||||
end
|
||||
|
||||
defp toggle_sensitive(%Activity{object: object} = activity, %{"sensitive" => sensitive})
|
||||
when is_boolean(sensitive) do
|
||||
new_data = Map.put(object.data, "sensitive", sensitive)
|
||||
|
||||
{:ok, object} =
|
||||
object
|
||||
|> Object.change(%{data: new_data})
|
||||
|> Object.update_and_set_cache()
|
||||
|
||||
{:ok, Map.put(activity, :object, object)}
|
||||
end
|
||||
|
||||
defp toggle_sensitive(activity, _), do: {:ok, activity}
|
||||
|
||||
defp set_visibility(activity, %{"visibility" => visibility}) do
|
||||
Utils.update_activity_visibility(activity, visibility)
|
||||
end
|
||||
|
||||
defp set_visibility(activity, _), do: {:ok, activity}
|
||||
|
||||
def hide_reblogs(user, muted) do
|
||||
ap_id = muted.ap_id
|
||||
|
||||
|
|
|
|||
|
|
@ -237,13 +237,11 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
"tag" => tags |> Enum.map(fn {_, tag} -> tag end) |> Enum.uniq()
|
||||
}
|
||||
|
||||
if in_reply_to do
|
||||
in_reply_to_object = Object.normalize(in_reply_to)
|
||||
|
||||
object
|
||||
|> Map.put("inReplyTo", in_reply_to_object.data["id"])
|
||||
with false <- is_nil(in_reply_to),
|
||||
%Object{} = in_reply_to_object <- Object.normalize(in_reply_to) do
|
||||
Map.put(object, "inReplyTo", in_reply_to_object.data["id"])
|
||||
else
|
||||
object
|
||||
_ -> object
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,13 @@ defmodule Pleroma.Web.Endpoint do
|
|||
# credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
|
||||
)
|
||||
|
||||
plug(Plug.Static.IndexHtml, at: "/pleroma/admin/")
|
||||
|
||||
plug(Plug.Static,
|
||||
at: "/pleroma/admin/",
|
||||
from: {:pleroma, "priv/static/adminfe/"}
|
||||
)
|
||||
|
||||
# Code reloading can be explicitly enabled under the
|
||||
# :code_reloader configuration of your endpoint.
|
||||
if code_reloading? do
|
||||
|
|
|
|||
|
|
@ -7,13 +7,10 @@ defmodule Pleroma.Web.Federator do
|
|||
alias Pleroma.Object.Containment
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Relay
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.Federator.Publisher
|
||||
alias Pleroma.Web.Federator.RetryQueue
|
||||
alias Pleroma.Web.OStatus
|
||||
alias Pleroma.Web.Salmon
|
||||
alias Pleroma.Web.WebFinger
|
||||
alias Pleroma.Web.Websub
|
||||
|
||||
|
|
@ -42,14 +39,6 @@ defmodule Pleroma.Web.Federator do
|
|||
PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:publish, activity], priority)
|
||||
end
|
||||
|
||||
def publish_single_ap(params) do
|
||||
PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:publish_single_ap, params])
|
||||
end
|
||||
|
||||
def publish_single_websub(websub) do
|
||||
PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:publish_single_websub, websub])
|
||||
end
|
||||
|
||||
def verify_websub(websub) do
|
||||
PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:verify_websub, websub])
|
||||
end
|
||||
|
|
@ -62,10 +51,6 @@ defmodule Pleroma.Web.Federator do
|
|||
PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:refresh_subscriptions])
|
||||
end
|
||||
|
||||
def publish_single_salmon(params) do
|
||||
PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:publish_single_salmon, params])
|
||||
end
|
||||
|
||||
# Job Worker Callbacks
|
||||
|
||||
def perform(:refresh_subscriptions) do
|
||||
|
|
@ -95,23 +80,7 @@ defmodule Pleroma.Web.Federator do
|
|||
with actor when not is_nil(actor) <- User.get_cached_by_ap_id(activity.data["actor"]) do
|
||||
{:ok, actor} = WebFinger.ensure_keys_present(actor)
|
||||
|
||||
if Visibility.is_public?(activity) do
|
||||
if OStatus.is_representable?(activity) do
|
||||
Logger.info(fn -> "Sending #{activity.data["id"]} out via WebSub" end)
|
||||
Websub.publish(Pleroma.Web.OStatus.feed_path(actor), actor, activity)
|
||||
|
||||
Logger.info(fn -> "Sending #{activity.data["id"]} out via Salmon" end)
|
||||
Pleroma.Web.Salmon.publish(actor, activity)
|
||||
end
|
||||
|
||||
if Keyword.get(Application.get_env(:pleroma, :instance), :allow_relay) do
|
||||
Logger.info(fn -> "Relaying #{activity.data["id"]} out" end)
|
||||
Relay.publish(activity)
|
||||
end
|
||||
end
|
||||
|
||||
Logger.info(fn -> "Sending #{activity.data["id"]} out via AP" end)
|
||||
Pleroma.Web.ActivityPub.ActivityPub.publish(actor, activity)
|
||||
Publisher.publish(actor, activity)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -148,25 +117,11 @@ defmodule Pleroma.Web.Federator do
|
|||
_e ->
|
||||
# Just drop those for now
|
||||
Logger.info("Unhandled activity")
|
||||
Logger.info(Poison.encode!(params, pretty: 2))
|
||||
Logger.info(Jason.encode!(params, pretty: true))
|
||||
:error
|
||||
end
|
||||
end
|
||||
|
||||
def perform(:publish_single_salmon, params) do
|
||||
Salmon.send_to_user(params)
|
||||
end
|
||||
|
||||
def perform(:publish_single_ap, params) do
|
||||
case ActivityPub.publish_one(params) do
|
||||
{:ok, _} ->
|
||||
:ok
|
||||
|
||||
{:error, _} ->
|
||||
RetryQueue.enqueue(params, ActivityPub)
|
||||
end
|
||||
end
|
||||
|
||||
def perform(
|
||||
:publish_single_websub,
|
||||
%{xml: _xml, topic: _topic, callback: _callback, secret: _secret} = params
|
||||
|
|
|
|||
95
lib/pleroma/web/federator/publisher.ex
Normal file
95
lib/pleroma/web/federator/publisher.ex
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Federator.Publisher do
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.Federator.RetryQueue
|
||||
|
||||
require Logger
|
||||
|
||||
@moduledoc """
|
||||
Defines the contract used by federation implementations to publish messages to
|
||||
their peers.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Determine whether an activity can be relayed using the federation module.
|
||||
"""
|
||||
@callback is_representable?(Pleroma.Activity.t()) :: boolean()
|
||||
|
||||
@doc """
|
||||
Relays an activity to a specified peer, determined by the parameters. The
|
||||
parameters used are controlled by the federation module.
|
||||
"""
|
||||
@callback publish_one(Map.t()) :: {:ok, Map.t()} | {:error, any()}
|
||||
|
||||
@doc """
|
||||
Enqueue publishing a single activity.
|
||||
"""
|
||||
@spec enqueue_one(module(), Map.t()) :: :ok
|
||||
def enqueue_one(module, %{} = params),
|
||||
do: PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:publish_one, module, params])
|
||||
|
||||
@spec perform(atom(), module(), any()) :: {:ok, any()} | {:error, any()}
|
||||
def perform(:publish_one, module, params) do
|
||||
case apply(module, :publish_one, [params]) do
|
||||
{:ok, _} ->
|
||||
:ok
|
||||
|
||||
{:error, _e} ->
|
||||
RetryQueue.enqueue(params, module)
|
||||
end
|
||||
end
|
||||
|
||||
def perform(type, _, _) do
|
||||
Logger.debug("Unknown task: #{type}")
|
||||
{:error, "Don't know what to do with this"}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Relays an activity to all specified peers.
|
||||
"""
|
||||
@callback publish(User.t(), Activity.t()) :: :ok | {:error, any()}
|
||||
|
||||
@spec publish(User.t(), Activity.t()) :: :ok
|
||||
def publish(%User{} = user, %Activity{} = activity) do
|
||||
Config.get([:instance, :federation_publisher_modules])
|
||||
|> Enum.each(fn module ->
|
||||
if module.is_representable?(activity) do
|
||||
Logger.info("Publishing #{activity.data["id"]} using #{inspect(module)}")
|
||||
module.publish(user, activity)
|
||||
end
|
||||
end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gathers links used by an outgoing federation module for WebFinger output.
|
||||
"""
|
||||
@callback gather_webfinger_links(User.t()) :: list()
|
||||
|
||||
@spec gather_webfinger_links(User.t()) :: list()
|
||||
def gather_webfinger_links(%User{} = user) do
|
||||
Config.get([:instance, :federation_publisher_modules])
|
||||
|> Enum.reduce([], fn module, links ->
|
||||
links ++ module.gather_webfinger_links(user)
|
||||
end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gathers nodeinfo protocol names supported by the federation module.
|
||||
"""
|
||||
@callback gather_nodeinfo_protocol_names() :: list()
|
||||
|
||||
@spec gather_nodeinfo_protocol_names() :: list()
|
||||
def gather_nodeinfo_protocol_names do
|
||||
Config.get([:instance, :federation_publisher_modules])
|
||||
|> Enum.reduce([], fn module, links ->
|
||||
links ++ module.gather_nodeinfo_protocol_names()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
# https://tools.ietf.org/html/draft-cavage-http-signatures-08
|
||||
defmodule Pleroma.Web.HTTPSignatures do
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
|
||||
require Logger
|
||||
|
||||
def split_signature(sig) do
|
||||
default = %{"headers" => "date"}
|
||||
|
||||
sig =
|
||||
sig
|
||||
|> String.trim()
|
||||
|> String.split(",")
|
||||
|> Enum.reduce(default, fn part, acc ->
|
||||
[key | rest] = String.split(part, "=")
|
||||
value = Enum.join(rest, "=")
|
||||
Map.put(acc, key, String.trim(value, "\""))
|
||||
end)
|
||||
|
||||
Map.put(sig, "headers", String.split(sig["headers"], ~r/\s/))
|
||||
end
|
||||
|
||||
def validate(headers, signature, public_key) do
|
||||
sigstring = build_signing_string(headers, signature["headers"])
|
||||
Logger.debug("Signature: #{signature["signature"]}")
|
||||
Logger.debug("Sigstring: #{sigstring}")
|
||||
{:ok, sig} = Base.decode64(signature["signature"])
|
||||
:public_key.verify(sigstring, :sha256, sig, public_key)
|
||||
end
|
||||
|
||||
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 <- Utils.get_ap_id(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 <- Utils.get_ap_id(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)
|
||||
end
|
||||
end
|
||||
else
|
||||
_e ->
|
||||
Logger.debug("Could not public key!")
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def validate_conn(conn, public_key) do
|
||||
headers = Enum.into(conn.req_headers, %{})
|
||||
signature = split_signature(headers["signature"])
|
||||
validate(headers, signature, public_key)
|
||||
end
|
||||
|
||||
def build_signing_string(headers, used_headers) do
|
||||
used_headers
|
||||
|> Enum.map(fn header -> "#{header}: #{headers[header]}" end)
|
||||
|> Enum.join("\n")
|
||||
end
|
||||
|
||||
def sign(user, headers) do
|
||||
with {:ok, %{info: %{keys: keys}}} <- Pleroma.Web.WebFinger.ensure_keys_present(user),
|
||||
{:ok, private_key, _} = Pleroma.Web.Salmon.keys_from_pem(keys) do
|
||||
sigstring = build_signing_string(headers, Map.keys(headers))
|
||||
|
||||
signature =
|
||||
:public_key.sign(sigstring, :sha256, private_key)
|
||||
|> Base.encode64()
|
||||
|
||||
[
|
||||
keyId: user.ap_id <> "#main-key",
|
||||
algorithm: "rsa-sha256",
|
||||
headers: Map.keys(headers) |> Enum.join(" "),
|
||||
signature: signature
|
||||
]
|
||||
|> Enum.map(fn {k, v} -> "#{k}=\"#{v}\"" end)
|
||||
|> Enum.join(",")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -39,12 +39,22 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
alias Pleroma.Web.OAuth.Authorization
|
||||
alias Pleroma.Web.OAuth.Scopes
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||
|
||||
alias Pleroma.Web.ControllerHelper
|
||||
import Ecto.Query
|
||||
|
||||
require Logger
|
||||
|
||||
plug(
|
||||
Pleroma.Plugs.RateLimitPlug,
|
||||
%{
|
||||
max_requests: Config.get([:app_account_creation, :max_requests]),
|
||||
interval: Config.get([:app_account_creation, :interval])
|
||||
}
|
||||
when action in [:account_register]
|
||||
)
|
||||
|
||||
@httpoison Application.get_env(:pleroma, :httpoison)
|
||||
@local_mastodon_name "Mastodon-Local"
|
||||
|
||||
|
|
@ -168,7 +178,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
@mastodon_api_level "2.6.5"
|
||||
@mastodon_api_level "2.7.2"
|
||||
|
||||
def masto_instance(conn, _params) do
|
||||
instance = Config.get(:instance)
|
||||
|
|
@ -293,7 +303,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
activities =
|
||||
[user.ap_id | user.following]
|
||||
|> ActivityPub.fetch_activities(params)
|
||||
|> ActivityPub.contain_timeline(user)
|
||||
|> Enum.reverse()
|
||||
|
||||
conn
|
||||
|
|
@ -1236,7 +1245,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
accounts
|
||||
|> Enum.each(fn account_id ->
|
||||
with %Pleroma.List{} = list <- Pleroma.List.get(id, user),
|
||||
%User{} = followed <- Pleroma.User.get_cached_by_id(account_id) do
|
||||
%User{} = followed <- User.get_cached_by_id(account_id) do
|
||||
Pleroma.List.unfollow(list, followed)
|
||||
end
|
||||
end)
|
||||
|
|
@ -1559,7 +1568,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
user_id: user.id,
|
||||
phrase: phrase,
|
||||
context: context,
|
||||
hide: Map.get(params, "irreversible", nil),
|
||||
hide: Map.get(params, "irreversible", false),
|
||||
whole_word: Map.get(params, "boolean", true)
|
||||
# expires_at
|
||||
}
|
||||
|
|
@ -1716,6 +1725,53 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def account_register(
|
||||
%{assigns: %{app: app}} = conn,
|
||||
%{"username" => nickname, "email" => _, "password" => _, "agreement" => true} = params
|
||||
) do
|
||||
params =
|
||||
params
|
||||
|> Map.take([
|
||||
"email",
|
||||
"captcha_solution",
|
||||
"captcha_token",
|
||||
"captcha_answer_data",
|
||||
"token",
|
||||
"password"
|
||||
])
|
||||
|> Map.put("nickname", nickname)
|
||||
|> Map.put("fullname", params["fullname"] || nickname)
|
||||
|> Map.put("bio", params["bio"] || "")
|
||||
|> Map.put("confirm", params["password"])
|
||||
|
||||
with {:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true),
|
||||
{:ok, token} <- Token.create_token(app, user, %{scopes: app.scopes}) do
|
||||
json(conn, %{
|
||||
token_type: "Bearer",
|
||||
access_token: token.token,
|
||||
scope: app.scopes,
|
||||
created_at: Token.Utils.format_created_at(token)
|
||||
})
|
||||
else
|
||||
{:error, errors} ->
|
||||
conn
|
||||
|> put_status(400)
|
||||
|> json(Jason.encode!(errors))
|
||||
end
|
||||
end
|
||||
|
||||
def account_register(%{assigns: %{app: _app}} = conn, _params) do
|
||||
conn
|
||||
|> put_status(400)
|
||||
|> json(%{error: "Missing parameters"})
|
||||
end
|
||||
|
||||
def account_register(conn, _) do
|
||||
conn
|
||||
|> put_status(403)
|
||||
|> json(%{error: "Invalid credentials"})
|
||||
end
|
||||
|
||||
def conversations(%{assigns: %{user: user}} = conn, params) do
|
||||
participations = Participation.for_user_with_last_activity_id(user, params)
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
follow_activity = Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, target)
|
||||
|
||||
requested =
|
||||
if follow_activity do
|
||||
if follow_activity && !User.following?(target, user) do
|
||||
follow_activity.data["state"] == "pending"
|
||||
else
|
||||
false
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
alias Pleroma.Web.MediaProxy
|
||||
|
||||
import Pleroma.Web.ActivityPub.Visibility, only: [get_visibility: 1]
|
||||
|
||||
# TODO: Add cached version.
|
||||
defp get_replied_to_activities(activities) do
|
||||
activities
|
||||
|
|
@ -340,30 +342,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
end
|
||||
end
|
||||
|
||||
def get_visibility(object) do
|
||||
public = "https://www.w3.org/ns/activitystreams#Public"
|
||||
to = object.data["to"] || []
|
||||
cc = object.data["cc"] || []
|
||||
|
||||
cond do
|
||||
public in to ->
|
||||
"public"
|
||||
|
||||
public in cc ->
|
||||
"unlisted"
|
||||
|
||||
# this should use the sql for the object's activity
|
||||
Enum.any?(to, &String.contains?(&1, "/followers")) ->
|
||||
"private"
|
||||
|
||||
length(cc) > 0 ->
|
||||
"private"
|
||||
|
||||
true ->
|
||||
"direct"
|
||||
end
|
||||
end
|
||||
|
||||
def render_content(%{data: %{"type" => "Video"}} = object) do
|
||||
with name when not is_nil(name) and name != "" <- object.data["name"] do
|
||||
"<p><a href=\"#{object.data["id"]}\">#{name}</a></p>#{object.data["content"]}"
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
|
|||
alias Pleroma.User
|
||||
alias Pleroma.Web
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
alias Pleroma.Web.Federator.Publisher
|
||||
|
||||
plug(Pleroma.Web.FederatingPlug)
|
||||
|
||||
|
|
@ -137,7 +138,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
|
|||
name: Pleroma.Application.name() |> String.downcase(),
|
||||
version: Pleroma.Application.version()
|
||||
},
|
||||
protocols: ["ostatus", "activitypub"],
|
||||
protocols: Publisher.gather_nodeinfo_protocol_names(),
|
||||
services: %{
|
||||
inbound: [],
|
||||
outbound: []
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.OAuth.App do
|
|||
import Ecto.Changeset
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
schema "apps" do
|
||||
field(:client_name, :string)
|
||||
field(:redirect_uris, :string)
|
||||
|
|
|
|||
|
|
@ -14,39 +14,57 @@ defmodule Pleroma.Web.OAuth.Authorization do
|
|||
import Ecto.Query
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
schema "oauth_authorizations" do
|
||||
field(:token, :string)
|
||||
field(:scopes, {:array, :string}, default: [])
|
||||
field(:valid_until, :naive_datetime_usec)
|
||||
field(:used, :boolean, default: false)
|
||||
belongs_to(:user, Pleroma.User, type: Pleroma.FlakeId)
|
||||
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||
belongs_to(:app, App)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
@spec create_authorization(App.t(), User.t() | %{}, [String.t()] | nil) ::
|
||||
{:ok, Authorization.t()} | {:error, Changeset.t()}
|
||||
def create_authorization(%App{} = app, %User{} = user, scopes \\ nil) do
|
||||
scopes = scopes || app.scopes
|
||||
token = :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false)
|
||||
|
||||
authorization = %Authorization{
|
||||
token: token,
|
||||
used: false,
|
||||
%{
|
||||
scopes: scopes || app.scopes,
|
||||
user_id: user.id,
|
||||
app_id: app.id,
|
||||
scopes: scopes,
|
||||
valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10)
|
||||
app_id: app.id
|
||||
}
|
||||
|
||||
Repo.insert(authorization)
|
||||
|> create_changeset()
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
@spec create_changeset(map()) :: Changeset.t()
|
||||
def create_changeset(attrs \\ %{}) do
|
||||
%Authorization{}
|
||||
|> cast(attrs, [:user_id, :app_id, :scopes, :valid_until])
|
||||
|> validate_required([:app_id, :scopes])
|
||||
|> add_token()
|
||||
|> add_lifetime()
|
||||
end
|
||||
|
||||
defp add_token(changeset) do
|
||||
token = :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false)
|
||||
put_change(changeset, :token, token)
|
||||
end
|
||||
|
||||
defp add_lifetime(changeset) do
|
||||
put_change(changeset, :valid_until, NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10))
|
||||
end
|
||||
|
||||
@spec use_changeset(Authtorizatiton.t(), map()) :: Changeset.t()
|
||||
def use_changeset(%Authorization{} = auth, params) do
|
||||
auth
|
||||
|> cast(params, [:used])
|
||||
|> validate_required([:used])
|
||||
end
|
||||
|
||||
@spec use_token(Authorization.t()) ::
|
||||
{:ok, Authorization.t()} | {:error, Changeset.t()} | {:error, String.t()}
|
||||
def use_token(%Authorization{used: false, valid_until: valid_until} = auth) do
|
||||
if NaiveDateTime.diff(NaiveDateTime.utc_now(), valid_until) < 0 do
|
||||
Repo.update(use_changeset(auth, %{used: true}))
|
||||
|
|
@ -57,6 +75,7 @@ defmodule Pleroma.Web.OAuth.Authorization do
|
|||
|
||||
def use_token(%Authorization{used: true}), do: {:error, "already used"}
|
||||
|
||||
@spec delete_user_authorizations(User.t()) :: {integer(), any()}
|
||||
def delete_user_authorizations(%User{id: user_id}) do
|
||||
from(
|
||||
a in Pleroma.Web.OAuth.Authorization,
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
|
||||
if Pleroma.Config.oauth_consumer_enabled?(), do: plug(Ueberauth)
|
||||
|
||||
@expires_in Pleroma.Config.get([:oauth2, :token_expires_in], 600)
|
||||
|
||||
plug(:fetch_session)
|
||||
plug(:fetch_flash)
|
||||
|
||||
|
|
@ -144,14 +142,14 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
@doc "Renew access_token with refresh_token"
|
||||
def token_exchange(
|
||||
conn,
|
||||
%{"grant_type" => "refresh_token", "refresh_token" => token} = params
|
||||
%{"grant_type" => "refresh_token", "refresh_token" => token} = _params
|
||||
) do
|
||||
with %App{} = app <- get_app_from_request(conn, params),
|
||||
with {:ok, app} <- Token.Utils.fetch_app(conn),
|
||||
{:ok, %{user: user} = token} <- Token.get_by_refresh_token(app, token),
|
||||
{:ok, token} <- RefreshToken.grant(token) do
|
||||
response_attrs = %{created_at: Token.Utils.format_created_at(token)}
|
||||
|
||||
json(conn, response_token(user, token, response_attrs))
|
||||
json(conn, Token.Response.build(user, token, response_attrs))
|
||||
else
|
||||
_error ->
|
||||
put_status(conn, 400)
|
||||
|
|
@ -160,14 +158,14 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
end
|
||||
|
||||
def token_exchange(conn, %{"grant_type" => "authorization_code"} = params) do
|
||||
with %App{} = app <- get_app_from_request(conn, params),
|
||||
with {:ok, app} <- Token.Utils.fetch_app(conn),
|
||||
fixed_token = Token.Utils.fix_padding(params["code"]),
|
||||
{:ok, auth} <- Authorization.get_by_token(app, fixed_token),
|
||||
%User{} = user <- User.get_cached_by_id(auth.user_id),
|
||||
{:ok, token} <- Token.exchange_token(app, auth) do
|
||||
response_attrs = %{created_at: Token.Utils.format_created_at(token)}
|
||||
|
||||
json(conn, response_token(user, token, response_attrs))
|
||||
json(conn, Token.Response.build(user, token, response_attrs))
|
||||
else
|
||||
_error ->
|
||||
put_status(conn, 400)
|
||||
|
|
@ -179,14 +177,14 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
conn,
|
||||
%{"grant_type" => "password"} = params
|
||||
) do
|
||||
with {_, {:ok, %User{} = user}} <- {:get_user, Authenticator.get_user(conn)},
|
||||
%App{} = app <- get_app_from_request(conn, params),
|
||||
with {:ok, %User{} = user} <- Authenticator.get_user(conn),
|
||||
{:ok, app} <- Token.Utils.fetch_app(conn),
|
||||
{:auth_active, true} <- {:auth_active, User.auth_active?(user)},
|
||||
{:user_active, true} <- {:user_active, !user.info.deactivated},
|
||||
{:ok, scopes} <- validate_scopes(app, params),
|
||||
{:ok, auth} <- Authorization.create_authorization(app, user, scopes),
|
||||
{:ok, token} <- Token.exchange_token(app, auth) do
|
||||
json(conn, response_token(user, token))
|
||||
json(conn, Token.Response.build(user, token))
|
||||
else
|
||||
{:auth_active, false} ->
|
||||
# Per https://github.com/tootsuite/mastodon/blob/
|
||||
|
|
@ -218,11 +216,23 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
token_exchange(conn, params)
|
||||
end
|
||||
|
||||
def token_exchange(conn, %{"grant_type" => "client_credentials"} = _params) do
|
||||
with {:ok, app} <- Token.Utils.fetch_app(conn),
|
||||
{:ok, auth} <- Authorization.create_authorization(app, %User{}),
|
||||
{:ok, token} <- Token.exchange_token(app, auth) do
|
||||
json(conn, Token.Response.build_for_client_credentials(token))
|
||||
else
|
||||
_error ->
|
||||
put_status(conn, 400)
|
||||
|> json(%{error: "Invalid credentials"})
|
||||
end
|
||||
end
|
||||
|
||||
# Bad request
|
||||
def token_exchange(conn, params), do: bad_request(conn, params)
|
||||
|
||||
def token_revoke(conn, %{"token" => _token} = params) do
|
||||
with %App{} = app <- get_app_from_request(conn, params),
|
||||
with {:ok, app} <- Token.Utils.fetch_app(conn),
|
||||
{:ok, _token} <- RevokeToken.revoke(app, params) do
|
||||
json(conn, %{})
|
||||
else
|
||||
|
|
@ -252,7 +262,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
auth_attrs
|
||||
|> Map.delete("scopes")
|
||||
|> Map.put("scope", scope)
|
||||
|> Poison.encode!()
|
||||
|> Jason.encode!()
|
||||
|
||||
params =
|
||||
auth_attrs
|
||||
|
|
@ -316,7 +326,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
end
|
||||
|
||||
defp callback_params(%{"state" => state} = params) do
|
||||
Map.merge(params, Poison.decode!(state))
|
||||
Map.merge(params, Jason.decode!(state))
|
||||
end
|
||||
|
||||
def registration_details(conn, %{"authorization" => auth_attrs}) do
|
||||
|
|
@ -405,33 +415,6 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
end
|
||||
end
|
||||
|
||||
defp get_app_from_request(conn, params) do
|
||||
conn
|
||||
|> fetch_client_credentials(params)
|
||||
|> fetch_client
|
||||
end
|
||||
|
||||
defp fetch_client({id, secret}) when is_binary(id) and is_binary(secret) do
|
||||
Repo.get_by(App, client_id: id, client_secret: secret)
|
||||
end
|
||||
|
||||
defp fetch_client({_id, _secret}), do: nil
|
||||
|
||||
defp fetch_client_credentials(conn, params) do
|
||||
# Per RFC 6749, HTTP Basic is preferred to body params
|
||||
with ["Basic " <> encoded] <- get_req_header(conn, "authorization"),
|
||||
{:ok, decoded} <- Base.decode64(encoded),
|
||||
[id, secret] <-
|
||||
Enum.map(
|
||||
String.split(decoded, ":"),
|
||||
fn s -> URI.decode_www_form(s) end
|
||||
) do
|
||||
{id, secret}
|
||||
else
|
||||
_ -> {params["client_id"], params["client_secret"]}
|
||||
end
|
||||
end
|
||||
|
||||
# Special case: Local MastodonFE
|
||||
defp redirect_uri(conn, "."), do: mastodon_api_url(conn, :login)
|
||||
|
||||
|
|
@ -442,18 +425,6 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
defp put_session_registration_id(conn, registration_id),
|
||||
do: put_session(conn, :registration_id, registration_id)
|
||||
|
||||
defp response_token(%User{} = user, token, opts \\ %{}) do
|
||||
%{
|
||||
token_type: "Bearer",
|
||||
access_token: token.token,
|
||||
refresh_token: token.refresh_token,
|
||||
expires_in: @expires_in,
|
||||
scope: Enum.join(token.scopes, " "),
|
||||
me: user.ap_id
|
||||
}
|
||||
|> Map.merge(opts)
|
||||
end
|
||||
|
||||
@spec validate_scopes(App.t(), map()) ::
|
||||
{:ok, list()} | {:error, :missing_scopes | :unsupported_scopes}
|
||||
defp validate_scopes(app, params) do
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ defmodule Pleroma.Web.OAuth.Token do
|
|||
field(:refresh_token, :string)
|
||||
field(:scopes, {:array, :string}, default: [])
|
||||
field(:valid_until, :naive_datetime_usec)
|
||||
belongs_to(:user, Pleroma.User, type: Pleroma.FlakeId)
|
||||
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||
belongs_to(:app, App)
|
||||
|
||||
timestamps()
|
||||
|
|
@ -45,12 +45,16 @@ defmodule Pleroma.Web.OAuth.Token do
|
|||
|> Repo.find_resource()
|
||||
end
|
||||
|
||||
@spec exchange_token(App.t(), Authorization.t()) ::
|
||||
{:ok, Token.t()} | {:error, Changeset.t()}
|
||||
def exchange_token(app, auth) do
|
||||
with {:ok, auth} <- Authorization.use_token(auth),
|
||||
true <- auth.app_id == app.id do
|
||||
user = if auth.user_id, do: User.get_cached_by_id(auth.user_id), else: %User{}
|
||||
|
||||
create_token(
|
||||
app,
|
||||
User.get_cached_by_id(auth.user_id),
|
||||
user,
|
||||
%{scopes: auth.scopes}
|
||||
)
|
||||
end
|
||||
|
|
@ -81,12 +85,13 @@ defmodule Pleroma.Web.OAuth.Token do
|
|||
|> validate_required([:valid_until])
|
||||
end
|
||||
|
||||
@spec create_token(App.t(), User.t(), map()) :: {:ok, Token} | {:error, Changeset.t()}
|
||||
def create_token(%App{} = app, %User{} = user, attrs \\ %{}) do
|
||||
%__MODULE__{user_id: user.id, app_id: app.id}
|
||||
|> cast(%{scopes: attrs[:scopes] || app.scopes}, [:scopes])
|
||||
|> validate_required([:scopes, :user_id, :app_id])
|
||||
|> validate_required([:scopes, :app_id])
|
||||
|> put_valid_until(attrs)
|
||||
|> put_token
|
||||
|> put_token()
|
||||
|> put_refresh_token(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
|
|
|||
32
lib/pleroma/web/oauth/token/response.ex
Normal file
32
lib/pleroma/web/oauth/token/response.ex
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
defmodule Pleroma.Web.OAuth.Token.Response do
|
||||
@moduledoc false
|
||||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.OAuth.Token.Utils
|
||||
|
||||
@expires_in Pleroma.Config.get([:oauth2, :token_expires_in], 600)
|
||||
|
||||
@doc false
|
||||
def build(%User{} = user, token, opts \\ %{}) do
|
||||
%{
|
||||
token_type: "Bearer",
|
||||
access_token: token.token,
|
||||
refresh_token: token.refresh_token,
|
||||
expires_in: @expires_in,
|
||||
scope: Enum.join(token.scopes, " "),
|
||||
me: user.ap_id
|
||||
}
|
||||
|> Map.merge(opts)
|
||||
end
|
||||
|
||||
def build_for_client_credentials(token) do
|
||||
%{
|
||||
token_type: "Bearer",
|
||||
access_token: token.token,
|
||||
refresh_token: token.refresh_token,
|
||||
created_at: Utils.format_created_at(token),
|
||||
expires_in: @expires_in,
|
||||
scope: Enum.join(token.scopes, " ")
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -3,6 +3,44 @@ defmodule Pleroma.Web.OAuth.Token.Utils do
|
|||
Auxiliary functions for dealing with tokens.
|
||||
"""
|
||||
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Web.OAuth.App
|
||||
|
||||
@doc "Fetch app by client credentials from request"
|
||||
@spec fetch_app(Plug.Conn.t()) :: {:ok, App.t()} | {:error, :not_found}
|
||||
def fetch_app(conn) do
|
||||
res =
|
||||
conn
|
||||
|> fetch_client_credentials()
|
||||
|> fetch_client
|
||||
|
||||
case res do
|
||||
%App{} = app -> {:ok, app}
|
||||
_ -> {:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
defp fetch_client({id, secret}) when is_binary(id) and is_binary(secret) do
|
||||
Repo.get_by(App, client_id: id, client_secret: secret)
|
||||
end
|
||||
|
||||
defp fetch_client({_id, _secret}), do: nil
|
||||
|
||||
defp fetch_client_credentials(conn) do
|
||||
# Per RFC 6749, HTTP Basic is preferred to body params
|
||||
with ["Basic " <> encoded] <- Plug.Conn.get_req_header(conn, "authorization"),
|
||||
{:ok, decoded} <- Base.decode64(encoded),
|
||||
[id, secret] <-
|
||||
Enum.map(
|
||||
String.split(decoded, ":"),
|
||||
fn s -> URI.decode_www_form(s) end
|
||||
) do
|
||||
{id, secret}
|
||||
else
|
||||
_ -> {conn.params["client_id"], conn.params["client_secret"]}
|
||||
end
|
||||
end
|
||||
|
||||
@doc "convert token inserted_at to unix timestamp"
|
||||
def format_created_at(%{inserted_at: inserted_at} = _token) do
|
||||
inserted_at
|
||||
|
|
|
|||
|
|
@ -18,15 +18,18 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
|
|||
end
|
||||
end
|
||||
|
||||
defp get_in_reply_to(%{"object" => %{"inReplyTo" => in_reply_to}}) do
|
||||
[
|
||||
{:"thr:in-reply-to",
|
||||
[ref: to_charlist(in_reply_to), href: to_charlist(get_href(in_reply_to))], []}
|
||||
]
|
||||
defp get_in_reply_to(activity) do
|
||||
with %Object{data: %{"inReplyTo" => in_reply_to}} <- Object.normalize(activity) do
|
||||
[
|
||||
{:"thr:in-reply-to",
|
||||
[ref: to_charlist(in_reply_to), href: to_charlist(get_href(in_reply_to))], []}
|
||||
]
|
||||
else
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
defp get_in_reply_to(_), do: []
|
||||
|
||||
defp get_mentions(to) do
|
||||
Enum.map(to, fn id ->
|
||||
cond do
|
||||
|
|
@ -98,7 +101,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
|
|||
[]}
|
||||
end)
|
||||
|
||||
in_reply_to = get_in_reply_to(activity.data)
|
||||
in_reply_to = get_in_reply_to(activity)
|
||||
author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
|
||||
mentions = activity.recipients |> get_mentions
|
||||
|
||||
|
|
@ -146,7 +149,6 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
|
|||
updated_at = activity.data["published"]
|
||||
inserted_at = activity.data["published"]
|
||||
|
||||
_in_reply_to = get_in_reply_to(activity.data)
|
||||
author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
|
||||
mentions = activity.recipients |> get_mentions
|
||||
|
||||
|
|
@ -177,7 +179,6 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
|
|||
updated_at = activity.data["published"]
|
||||
inserted_at = activity.data["published"]
|
||||
|
||||
_in_reply_to = get_in_reply_to(activity.data)
|
||||
author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
|
||||
|
||||
retweeted_activity = Activity.get_create_by_object_ap_id(activity.data["object"])
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ defmodule Pleroma.Web.OStatus do
|
|||
alias Pleroma.Web
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.OStatus.DeleteHandler
|
||||
alias Pleroma.Web.OStatus.FollowHandler
|
||||
alias Pleroma.Web.OStatus.NoteHandler
|
||||
|
|
@ -30,7 +31,7 @@ defmodule Pleroma.Web.OStatus do
|
|||
is_nil(object) ->
|
||||
false
|
||||
|
||||
object.data["type"] == "Note" ->
|
||||
Visibility.is_public?(activity) && object.data["type"] == "Note" ->
|
||||
true
|
||||
|
||||
true ->
|
||||
|
|
|
|||
|
|
@ -34,4 +34,6 @@ defmodule Pleroma.Web.RichMedia.Helpers do
|
|||
end
|
||||
|
||||
def fetch_data_for_activity(_), do: %{}
|
||||
|
||||
def perform(:fetch, %Activity{} = activity), do: fetch_data_for_activity(activity)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -84,11 +84,13 @@ defmodule Pleroma.Web.Router do
|
|||
plug(Pleroma.Plugs.EnsureUserKeyPlug)
|
||||
end
|
||||
|
||||
pipeline :oauth_read_or_unauthenticated do
|
||||
pipeline :oauth_read_or_public do
|
||||
plug(Pleroma.Plugs.OAuthScopesPlug, %{
|
||||
scopes: ["read"],
|
||||
fallback: :proceed_unauthenticated
|
||||
})
|
||||
|
||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||
end
|
||||
|
||||
pipeline :oauth_read do
|
||||
|
|
@ -146,34 +148,60 @@ defmodule Pleroma.Web.Router do
|
|||
scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do
|
||||
pipe_through([:admin_api, :oauth_write])
|
||||
|
||||
post("/user/follow", AdminAPIController, :user_follow)
|
||||
post("/user/unfollow", AdminAPIController, :user_unfollow)
|
||||
|
||||
get("/users", AdminAPIController, :list_users)
|
||||
get("/users/:nickname", AdminAPIController, :user_show)
|
||||
post("/users/follow", AdminAPIController, :user_follow)
|
||||
post("/users/unfollow", AdminAPIController, :user_unfollow)
|
||||
|
||||
# TODO: to be removed at version 1.0
|
||||
delete("/user", AdminAPIController, :user_delete)
|
||||
patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)
|
||||
post("/user", AdminAPIController, :user_create)
|
||||
|
||||
delete("/users", AdminAPIController, :user_delete)
|
||||
post("/users", AdminAPIController, :user_create)
|
||||
patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)
|
||||
put("/users/tag", AdminAPIController, :tag_users)
|
||||
delete("/users/tag", AdminAPIController, :untag_users)
|
||||
|
||||
# TODO: to be removed at version 1.0
|
||||
get("/permission_group/:nickname", AdminAPIController, :right_get)
|
||||
get("/permission_group/:nickname/:permission_group", AdminAPIController, :right_get)
|
||||
post("/permission_group/:nickname/:permission_group", AdminAPIController, :right_add)
|
||||
delete("/permission_group/:nickname/:permission_group", AdminAPIController, :right_delete)
|
||||
|
||||
put("/activation_status/:nickname", AdminAPIController, :set_activation_status)
|
||||
get("/users/:nickname/permission_group", AdminAPIController, :right_get)
|
||||
get("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_get)
|
||||
post("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_add)
|
||||
|
||||
delete(
|
||||
"/users/:nickname/permission_group/:permission_group",
|
||||
AdminAPIController,
|
||||
:right_delete
|
||||
)
|
||||
|
||||
put("/users/:nickname/activation_status", AdminAPIController, :set_activation_status)
|
||||
|
||||
post("/relay", AdminAPIController, :relay_follow)
|
||||
delete("/relay", AdminAPIController, :relay_unfollow)
|
||||
|
||||
get("/invite_token", AdminAPIController, :get_invite_token)
|
||||
get("/invites", AdminAPIController, :invites)
|
||||
post("/revoke_invite", AdminAPIController, :revoke_invite)
|
||||
post("/email_invite", AdminAPIController, :email_invite)
|
||||
get("/users/invite_token", AdminAPIController, :get_invite_token)
|
||||
get("/users/invites", AdminAPIController, :invites)
|
||||
post("/users/revoke_invite", AdminAPIController, :revoke_invite)
|
||||
post("/users/email_invite", AdminAPIController, :email_invite)
|
||||
|
||||
# TODO: to be removed at version 1.0
|
||||
get("/password_reset", AdminAPIController, :get_password_reset)
|
||||
|
||||
get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
|
||||
|
||||
get("/users", AdminAPIController, :list_users)
|
||||
get("/users/:nickname", AdminAPIController, :user_show)
|
||||
|
||||
get("/reports", AdminAPIController, :list_reports)
|
||||
get("/reports/:id", AdminAPIController, :report_show)
|
||||
put("/reports/:id", AdminAPIController, :report_update_state)
|
||||
post("/reports/:id/respond", AdminAPIController, :report_respond)
|
||||
|
||||
put("/statuses/:id", AdminAPIController, :status_update)
|
||||
delete("/statuses/:id", AdminAPIController, :status_delete)
|
||||
end
|
||||
|
||||
scope "/", Pleroma.Web.TwitterAPI do
|
||||
|
|
@ -197,6 +225,7 @@ defmodule Pleroma.Web.Router do
|
|||
post("/change_password", UtilController, :change_password)
|
||||
post("/delete_account", UtilController, :delete_account)
|
||||
put("/notification_settings", UtilController, :update_notificaton_settings)
|
||||
post("/disable_account", UtilController, :disable_account)
|
||||
end
|
||||
|
||||
scope [] do
|
||||
|
|
@ -367,6 +396,8 @@ defmodule Pleroma.Web.Router do
|
|||
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
||||
pipe_through(:api)
|
||||
|
||||
post("/accounts", MastodonAPIController, :account_register)
|
||||
|
||||
get("/instance", MastodonAPIController, :masto_instance)
|
||||
get("/instance/peers", MastodonAPIController, :peers)
|
||||
post("/apps", MastodonAPIController, :create_app)
|
||||
|
|
@ -383,7 +414,7 @@ defmodule Pleroma.Web.Router do
|
|||
get("/accounts/search", MastodonAPIController, :account_search)
|
||||
|
||||
scope [] do
|
||||
pipe_through(:oauth_read_or_unauthenticated)
|
||||
pipe_through(:oauth_read_or_public)
|
||||
|
||||
get("/timelines/public", MastodonAPIController, :public_timeline)
|
||||
get("/timelines/tag/:tag", MastodonAPIController, :hashtag_timeline)
|
||||
|
|
@ -404,7 +435,7 @@ defmodule Pleroma.Web.Router do
|
|||
end
|
||||
|
||||
scope "/api/v2", Pleroma.Web.MastodonAPI do
|
||||
pipe_through([:api, :oauth_read_or_unauthenticated])
|
||||
pipe_through([:api, :oauth_read_or_public])
|
||||
get("/search", MastodonAPIController, :search2)
|
||||
end
|
||||
|
||||
|
|
@ -434,7 +465,7 @@ defmodule Pleroma.Web.Router do
|
|||
)
|
||||
|
||||
scope [] do
|
||||
pipe_through(:oauth_read_or_unauthenticated)
|
||||
pipe_through(:oauth_read_or_public)
|
||||
|
||||
get("/statuses/user_timeline", TwitterAPI.Controller, :user_timeline)
|
||||
get("/qvitter/statuses/user_timeline", TwitterAPI.Controller, :user_timeline)
|
||||
|
|
@ -452,7 +483,7 @@ defmodule Pleroma.Web.Router do
|
|||
end
|
||||
|
||||
scope "/api", Pleroma.Web do
|
||||
pipe_through([:api, :oauth_read_or_unauthenticated])
|
||||
pipe_through([:api, :oauth_read_or_public])
|
||||
|
||||
get("/statuses/public_timeline", TwitterAPI.Controller, :public_timeline)
|
||||
|
||||
|
|
@ -466,7 +497,7 @@ defmodule Pleroma.Web.Router do
|
|||
end
|
||||
|
||||
scope "/api", Pleroma.Web, as: :twitter_api_search do
|
||||
pipe_through([:api, :oauth_read_or_unauthenticated])
|
||||
pipe_through([:api, :oauth_read_or_public])
|
||||
get("/pleroma/search_user", TwitterAPI.Controller, :search_user)
|
||||
end
|
||||
|
||||
|
|
@ -650,7 +681,7 @@ defmodule Pleroma.Web.Router do
|
|||
delete("/auth/sign_out", MastodonAPIController, :logout)
|
||||
|
||||
scope [] do
|
||||
pipe_through(:oauth_read_or_unauthenticated)
|
||||
pipe_through(:oauth_read_or_public)
|
||||
get("/web/*path", MastodonAPIController, :index)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,12 +3,18 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Salmon do
|
||||
@behaviour Pleroma.Web.Federator.Publisher
|
||||
|
||||
@httpoison Application.get_env(:pleroma, :httpoison)
|
||||
|
||||
use Bitwise
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Instances
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.Federator.Publisher
|
||||
alias Pleroma.Web.OStatus
|
||||
alias Pleroma.Web.OStatus.ActivityRepresenter
|
||||
alias Pleroma.Web.XML
|
||||
|
||||
|
|
@ -165,12 +171,12 @@ defmodule Pleroma.Web.Salmon do
|
|||
end
|
||||
|
||||
@doc "Pushes an activity to remote account."
|
||||
def send_to_user(%{recipient: %{info: %{salmon: salmon}}} = params),
|
||||
do: send_to_user(Map.put(params, :recipient, salmon))
|
||||
def publish_one(%{recipient: %{info: %{salmon: salmon}}} = params),
|
||||
do: publish_one(Map.put(params, :recipient, salmon))
|
||||
|
||||
def send_to_user(%{recipient: url, feed: feed, poster: poster} = params) when is_binary(url) do
|
||||
def publish_one(%{recipient: url, feed: feed} = params) when is_binary(url) do
|
||||
with {:ok, %{status: code}} when code in 200..299 <-
|
||||
poster.(
|
||||
@httpoison.post(
|
||||
url,
|
||||
feed,
|
||||
[{"Content-Type", "application/magic-envelope+xml"}]
|
||||
|
|
@ -184,11 +190,11 @@ defmodule Pleroma.Web.Salmon do
|
|||
e ->
|
||||
unless params[:unreachable_since], do: Instances.set_reachable(url)
|
||||
Logger.debug(fn -> "Pushing Salmon to #{url} failed, #{inspect(e)}" end)
|
||||
:error
|
||||
{:error, "Unreachable instance"}
|
||||
end
|
||||
end
|
||||
|
||||
def send_to_user(_), do: :noop
|
||||
def publish_one(_), do: :noop
|
||||
|
||||
@supported_activities [
|
||||
"Create",
|
||||
|
|
@ -199,13 +205,19 @@ defmodule Pleroma.Web.Salmon do
|
|||
"Delete"
|
||||
]
|
||||
|
||||
def is_representable?(%Activity{data: %{"type" => type}} = activity)
|
||||
when type in @supported_activities,
|
||||
do: Visibility.is_public?(activity)
|
||||
|
||||
def is_representable?(_), do: false
|
||||
|
||||
@doc """
|
||||
Publishes an activity to remote accounts
|
||||
"""
|
||||
@spec publish(User.t(), Pleroma.Activity.t(), Pleroma.HTTP.t()) :: none
|
||||
def publish(user, activity, poster \\ &@httpoison.post/3)
|
||||
@spec publish(User.t(), Pleroma.Activity.t()) :: none
|
||||
def publish(user, activity)
|
||||
|
||||
def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity, poster)
|
||||
def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity)
|
||||
when type in @supported_activities do
|
||||
feed = ActivityRepresenter.to_simple_form(activity, user, true)
|
||||
|
||||
|
|
@ -229,15 +241,29 @@ defmodule Pleroma.Web.Salmon do
|
|||
|> Enum.each(fn remote_user ->
|
||||
Logger.debug(fn -> "Sending Salmon to #{remote_user.ap_id}" end)
|
||||
|
||||
Pleroma.Web.Federator.publish_single_salmon(%{
|
||||
Publisher.enqueue_one(__MODULE__, %{
|
||||
recipient: remote_user,
|
||||
feed: feed,
|
||||
poster: poster,
|
||||
unreachable_since: reachable_urls_metadata[remote_user.info.salmon]
|
||||
})
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
def publish(%{id: id}, _, _), do: Logger.debug(fn -> "Keys missing for user #{id}" end)
|
||||
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)
|
||||
magic_key = encode_key(public)
|
||||
|
||||
[
|
||||
%{"rel" => "salmon", "href" => OStatus.salmon_path(user)},
|
||||
%{
|
||||
"rel" => "magic-public-key",
|
||||
"href" => "data:application/magic-public-key,#{magic_key}"
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
def gather_nodeinfo_protocol_names, do: []
|
||||
end
|
||||
|
|
|
|||
|
|
@ -173,8 +173,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
|
||||
def config(conn, _params) do
|
||||
instance = Pleroma.Config.get(:instance)
|
||||
instance_fe = Pleroma.Config.get(:fe)
|
||||
instance_chat = Pleroma.Config.get(:chat)
|
||||
|
||||
case get_format(conn) do
|
||||
"xml" ->
|
||||
|
|
@ -219,31 +217,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
if(Pleroma.Config.get([:instance, :safe_dm_mentions]), do: "1", else: "0")
|
||||
}
|
||||
|
||||
pleroma_fe =
|
||||
if instance_fe do
|
||||
%{
|
||||
theme: Keyword.get(instance_fe, :theme),
|
||||
background: Keyword.get(instance_fe, :background),
|
||||
logo: Keyword.get(instance_fe, :logo),
|
||||
logoMask: Keyword.get(instance_fe, :logo_mask),
|
||||
logoMargin: Keyword.get(instance_fe, :logo_margin),
|
||||
redirectRootNoLogin: Keyword.get(instance_fe, :redirect_root_no_login),
|
||||
redirectRootLogin: Keyword.get(instance_fe, :redirect_root_login),
|
||||
chatDisabled: !Keyword.get(instance_chat, :enabled),
|
||||
showInstanceSpecificPanel: Keyword.get(instance_fe, :show_instance_panel),
|
||||
scopeOptionsEnabled: Keyword.get(instance_fe, :scope_options_enabled),
|
||||
formattingOptionsEnabled: Keyword.get(instance_fe, :formatting_options_enabled),
|
||||
collapseMessageWithSubject:
|
||||
Keyword.get(instance_fe, :collapse_message_with_subject),
|
||||
hidePostStats: Keyword.get(instance_fe, :hide_post_stats),
|
||||
hideUserStats: Keyword.get(instance_fe, :hide_user_stats),
|
||||
scopeCopy: Keyword.get(instance_fe, :scope_copy),
|
||||
subjectLineBehavior: Keyword.get(instance_fe, :subject_line_behavior),
|
||||
alwaysShowSubjectInput: Keyword.get(instance_fe, :always_show_subject_input)
|
||||
}
|
||||
else
|
||||
Pleroma.Config.get([:frontend_configurations, :pleroma_fe])
|
||||
end
|
||||
pleroma_fe = Pleroma.Config.get([:frontend_configurations, :pleroma_fe])
|
||||
|
||||
managed_config = Keyword.get(instance, :managed_config)
|
||||
|
||||
|
|
@ -309,8 +283,13 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
Enum.map(lines, fn line ->
|
||||
String.split(line, ",") |> List.first()
|
||||
end)
|
||||
|> List.delete("Account address"),
|
||||
{:ok, _} = Task.start(fn -> User.follow_import(follower, followed_identifiers) end) do
|
||||
|> List.delete("Account address") do
|
||||
PleromaJobQueue.enqueue(:background, User, [
|
||||
:follow_import,
|
||||
follower,
|
||||
followed_identifiers
|
||||
])
|
||||
|
||||
json(conn, "job started")
|
||||
end
|
||||
end
|
||||
|
|
@ -320,8 +299,13 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
end
|
||||
|
||||
def blocks_import(%{assigns: %{user: blocker}} = conn, %{"list" => list}) do
|
||||
with blocked_identifiers <- String.split(list),
|
||||
{:ok, _} = Task.start(fn -> User.blocks_import(blocker, blocked_identifiers) end) do
|
||||
with blocked_identifiers <- String.split(list) do
|
||||
PleromaJobQueue.enqueue(:background, User, [
|
||||
:blocks_import,
|
||||
blocker,
|
||||
blocked_identifiers
|
||||
])
|
||||
|
||||
json(conn, "job started")
|
||||
end
|
||||
end
|
||||
|
|
@ -360,6 +344,17 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
end
|
||||
end
|
||||
|
||||
def disable_account(%{assigns: %{user: user}} = conn, params) do
|
||||
case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
|
||||
{:ok, user} ->
|
||||
User.deactivate_async(user)
|
||||
json(conn, %{status: "success"})
|
||||
|
||||
{:error, msg} ->
|
||||
json(conn, %{error: msg})
|
||||
end
|
||||
end
|
||||
|
||||
def captcha(conn, _params) do
|
||||
json(conn, Pleroma.Captcha.new())
|
||||
end
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||
end
|
||||
end
|
||||
|
||||
def register_user(params) do
|
||||
def register_user(params, opts \\ []) do
|
||||
token = params["token"]
|
||||
|
||||
params = %{
|
||||
|
|
@ -162,13 +162,22 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||
# I have no idea how this error handling works
|
||||
{:error, %{error: Jason.encode!(%{captcha: [error]})}}
|
||||
else
|
||||
registrations_open = Pleroma.Config.get([:instance, :registrations_open])
|
||||
registration_process(registrations_open, params, token)
|
||||
registration_process(
|
||||
params,
|
||||
%{
|
||||
registrations_open: Pleroma.Config.get([:instance, :registrations_open]),
|
||||
token: token
|
||||
},
|
||||
opts
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
defp registration_process(registration_open, params, token)
|
||||
when registration_open == false or is_nil(registration_open) do
|
||||
defp registration_process(params, %{registrations_open: true}, opts) do
|
||||
create_user(params, opts)
|
||||
end
|
||||
|
||||
defp registration_process(params, %{token: token}, opts) do
|
||||
invite =
|
||||
unless is_nil(token) do
|
||||
Repo.get_by(UserInviteToken, %{token: token})
|
||||
|
|
@ -182,19 +191,15 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||
|
||||
invite when valid_invite? ->
|
||||
UserInviteToken.update_usage!(invite)
|
||||
create_user(params)
|
||||
create_user(params, opts)
|
||||
|
||||
_ ->
|
||||
{:error, "Expired token"}
|
||||
end
|
||||
end
|
||||
|
||||
defp registration_process(true, params, _token) do
|
||||
create_user(params)
|
||||
end
|
||||
|
||||
defp create_user(params) do
|
||||
changeset = User.register_changeset(%User{}, params)
|
||||
defp create_user(params, opts) do
|
||||
changeset = User.register_changeset(%User{}, params, opts)
|
||||
|
||||
case User.register(changeset) do
|
||||
{:ok, user} ->
|
||||
|
|
@ -231,12 +236,15 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||
def get_user(user \\ nil, params) do
|
||||
case params do
|
||||
%{"user_id" => user_id} ->
|
||||
case target = User.get_cached_by_nickname_or_id(user_id) do
|
||||
case User.get_cached_by_nickname_or_id(user_id) do
|
||||
nil ->
|
||||
{:error, "No user with such user_id"}
|
||||
|
||||
_ ->
|
||||
{:ok, target}
|
||||
%User{info: %{deactivated: true}} ->
|
||||
{:error, "User has been disabled"}
|
||||
|
||||
user ->
|
||||
{:ok, user}
|
||||
end
|
||||
|
||||
%{"screen_name" => nickname} ->
|
||||
|
|
|
|||
|
|
@ -101,9 +101,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("user", user)
|
||||
|
||||
activities =
|
||||
ActivityPub.fetch_activities([user.ap_id | user.following], params)
|
||||
|> ActivityPub.contain_timeline(user)
|
||||
activities = ActivityPub.fetch_activities([user.ap_id | user.following], params)
|
||||
|
||||
conn
|
||||
|> put_view(ActivityView)
|
||||
|
|
@ -440,7 +438,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
true <- user.local,
|
||||
true <- user.info.confirmation_pending,
|
||||
true <- user.info.confirmation_token == token,
|
||||
info_change <- User.Info.confirmation_changeset(user.info, :confirmed),
|
||||
info_change <- User.Info.confirmation_changeset(user.info, need_confirmation: false),
|
||||
changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_change),
|
||||
{:ok, _} <- User.update_and_set_cache(changeset) do
|
||||
conn
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
|
|||
"tags" => tags,
|
||||
"activity_type" => "post",
|
||||
"possibly_sensitive" => possibly_sensitive,
|
||||
"visibility" => StatusView.get_visibility(object),
|
||||
"visibility" => Pleroma.Web.ActivityPub.Visibility.get_visibility(object),
|
||||
"summary" => summary,
|
||||
"summary_html" => summary |> Formatter.emojify(object.data["emoji"]),
|
||||
"card" => card,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ defmodule Pleroma.Web.WebFinger do
|
|||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web
|
||||
alias Pleroma.Web.OStatus
|
||||
alias Pleroma.Web.Federator.Publisher
|
||||
alias Pleroma.Web.Salmon
|
||||
alias Pleroma.Web.XML
|
||||
alias Pleroma.XmlBuilder
|
||||
|
|
@ -50,70 +50,40 @@ defmodule Pleroma.Web.WebFinger do
|
|||
end
|
||||
end
|
||||
|
||||
defp gather_links(%User{} = user) do
|
||||
[
|
||||
%{
|
||||
"rel" => "http://webfinger.net/rel/profile-page",
|
||||
"type" => "text/html",
|
||||
"href" => user.ap_id
|
||||
}
|
||||
] ++ Publisher.gather_webfinger_links(user)
|
||||
end
|
||||
|
||||
def represent_user(user, "JSON") do
|
||||
{:ok, user} = ensure_keys_present(user)
|
||||
{:ok, _private, public} = Salmon.keys_from_pem(user.info.keys)
|
||||
magic_key = Salmon.encode_key(public)
|
||||
|
||||
%{
|
||||
"subject" => "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}",
|
||||
"aliases" => [user.ap_id],
|
||||
"links" => [
|
||||
%{
|
||||
"rel" => "http://schemas.google.com/g/2010#updates-from",
|
||||
"type" => "application/atom+xml",
|
||||
"href" => OStatus.feed_path(user)
|
||||
},
|
||||
%{
|
||||
"rel" => "http://webfinger.net/rel/profile-page",
|
||||
"type" => "text/html",
|
||||
"href" => user.ap_id
|
||||
},
|
||||
%{"rel" => "salmon", "href" => OStatus.salmon_path(user)},
|
||||
%{
|
||||
"rel" => "magic-public-key",
|
||||
"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()
|
||||
}
|
||||
]
|
||||
"links" => gather_links(user)
|
||||
}
|
||||
end
|
||||
|
||||
def represent_user(user, "XML") do
|
||||
{:ok, user} = ensure_keys_present(user)
|
||||
{:ok, _private, public} = Salmon.keys_from_pem(user.info.keys)
|
||||
magic_key = Salmon.encode_key(public)
|
||||
|
||||
links =
|
||||
gather_links(user)
|
||||
|> Enum.map(fn link -> {:Link, link} end)
|
||||
|
||||
{
|
||||
:XRD,
|
||||
%{xmlns: "http://docs.oasis-open.org/ns/xri/xrd-1.0"},
|
||||
[
|
||||
{:Subject, "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}"},
|
||||
{:Alias, user.ap_id},
|
||||
{:Link,
|
||||
%{
|
||||
rel: "http://schemas.google.com/g/2010#updates-from",
|
||||
type: "application/atom+xml",
|
||||
href: OStatus.feed_path(user)
|
||||
}},
|
||||
{:Link,
|
||||
%{rel: "http://webfinger.net/rel/profile-page", type: "text/html", href: user.ap_id}},
|
||||
{:Link, %{rel: "salmon", href: OStatus.salmon_path(user)}},
|
||||
{:Link,
|
||||
%{rel: "magic-public-key", href: "data:application/magic-public-key,#{magic_key}"}},
|
||||
{:Link, %{rel: "self", type: "application/activity+json", href: user.ap_id}},
|
||||
{:Link,
|
||||
%{rel: "http://ostatus.org/schema/1.0/subscribe", template: OStatus.remote_follow_path()}}
|
||||
]
|
||||
{:Alias, user.ap_id}
|
||||
] ++ links
|
||||
}
|
||||
|> XmlBuilder.to_doc()
|
||||
end
|
||||
|
|
@ -129,7 +99,7 @@ defmodule Pleroma.Web.WebFinger do
|
|||
|
||||
info_cng =
|
||||
info
|
||||
|> Pleroma.User.Info.set_keys(pem)
|
||||
|> User.Info.set_keys(pem)
|
||||
|
||||
cng =
|
||||
Ecto.Changeset.change(user)
|
||||
|
|
|
|||
|
|
@ -4,10 +4,14 @@
|
|||
|
||||
defmodule Pleroma.Web.Websub do
|
||||
alias Ecto.Changeset
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Instances
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.Endpoint
|
||||
alias Pleroma.Web.Federator
|
||||
alias Pleroma.Web.Federator.Publisher
|
||||
alias Pleroma.Web.OStatus
|
||||
alias Pleroma.Web.OStatus.FeedRepresenter
|
||||
alias Pleroma.Web.Router.Helpers
|
||||
|
|
@ -18,6 +22,8 @@ defmodule Pleroma.Web.Websub do
|
|||
|
||||
import Ecto.Query
|
||||
|
||||
@behaviour Pleroma.Web.Federator.Publisher
|
||||
|
||||
@httpoison Application.get_env(:pleroma, :httpoison)
|
||||
|
||||
def verify(subscription, getter \\ &@httpoison.get/3) do
|
||||
|
|
@ -56,6 +62,13 @@ defmodule Pleroma.Web.Websub do
|
|||
"Undo",
|
||||
"Delete"
|
||||
]
|
||||
|
||||
def is_representable?(%Activity{data: %{"type" => type}} = activity)
|
||||
when type in @supported_activities,
|
||||
do: Visibility.is_public?(activity)
|
||||
|
||||
def is_representable?(_), do: false
|
||||
|
||||
def publish(topic, user, %{data: %{"type" => type}} = activity)
|
||||
when type in @supported_activities do
|
||||
response =
|
||||
|
|
@ -88,12 +101,14 @@ defmodule Pleroma.Web.Websub do
|
|||
unreachable_since: reachable_callbacks_metadata[sub.callback]
|
||||
}
|
||||
|
||||
Federator.publish_single_websub(data)
|
||||
Publisher.enqueue_one(__MODULE__, data)
|
||||
end)
|
||||
end
|
||||
|
||||
def publish(_, _, _), do: ""
|
||||
|
||||
def publish(actor, activity), do: publish(Pleroma.Web.OStatus.feed_path(actor), actor, activity)
|
||||
|
||||
def sign(secret, doc) do
|
||||
:crypto.hmac(:sha, secret, to_string(doc)) |> Base.encode16() |> String.downcase()
|
||||
end
|
||||
|
|
@ -299,4 +314,20 @@ defmodule Pleroma.Web.Websub do
|
|||
{:error, response}
|
||||
end
|
||||
end
|
||||
|
||||
def gather_webfinger_links(%User{} = user) do
|
||||
[
|
||||
%{
|
||||
"rel" => "http://schemas.google.com/g/2010#updates-from",
|
||||
"type" => "application/atom+xml",
|
||||
"href" => OStatus.feed_path(user)
|
||||
},
|
||||
%{
|
||||
"rel" => "http://ostatus.org/schema/1.0/subscribe",
|
||||
"template" => OStatus.remote_follow_path()
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
def gather_nodeinfo_protocol_names, do: ["ostatus"]
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue