websub: remove entirely
This commit is contained in:
parent
b16a460916
commit
4f82e42e4e
15 changed files with 1 additions and 927 deletions
|
@ -28,7 +28,6 @@ defmodule Pleroma.User do
|
|||
alias Pleroma.Web.OAuth
|
||||
alias Pleroma.Web.OStatus
|
||||
alias Pleroma.Web.RelMe
|
||||
alias Pleroma.Web.Websub
|
||||
alias Pleroma.Workers.BackgroundWorker
|
||||
|
||||
require Logger
|
||||
|
@ -437,12 +436,6 @@ def follow(%User{} = follower, %User{info: info} = followed) do
|
|||
{:error, "Could not follow user: #{followed.nickname} blocked you."}
|
||||
|
||||
true ->
|
||||
benchmark? = Pleroma.Config.get([:env]) == :benchmark
|
||||
|
||||
if !followed.local && follower.local && !ap_enabled?(followed) && !benchmark? do
|
||||
Websub.subscribe(follower, followed)
|
||||
end
|
||||
|
||||
q =
|
||||
from(u in User,
|
||||
where: u.id == ^follower.id,
|
||||
|
|
|
@ -1073,8 +1073,6 @@ def perform(:user_upgrade, user) do
|
|||
|
||||
Repo.update_all(q, [])
|
||||
|
||||
maybe_retire_websub(user.ap_id)
|
||||
|
||||
q =
|
||||
from(
|
||||
a in Activity,
|
||||
|
@ -1117,19 +1115,6 @@ defp upgrade_user(user, data) do
|
|||
|> User.update_and_set_cache()
|
||||
end
|
||||
|
||||
def maybe_retire_websub(ap_id) do
|
||||
# some sanity checks
|
||||
if is_binary(ap_id) && String.length(ap_id) > 8 do
|
||||
q =
|
||||
from(
|
||||
ws in Pleroma.Web.Websub.WebsubClientSubscription,
|
||||
where: fragment("? like ?", ws.topic, ^"#{ap_id}%")
|
||||
)
|
||||
|
||||
Repo.delete_all(q)
|
||||
end
|
||||
end
|
||||
|
||||
def maybe_fix_user_url(%{"url" => url} = data) when is_map(url) do
|
||||
Map.put(data, "url", url["href"])
|
||||
end
|
||||
|
|
|
@ -19,7 +19,6 @@ defmodule Pleroma.Web.OStatus do
|
|||
alias Pleroma.Web.OStatus.NoteHandler
|
||||
alias Pleroma.Web.OStatus.UnfollowHandler
|
||||
alias Pleroma.Web.WebFinger
|
||||
alias Pleroma.Web.Websub
|
||||
|
||||
def is_representable?(%Activity{} = activity) do
|
||||
object = Object.normalize(activity)
|
||||
|
@ -314,11 +313,9 @@ def make_avatar_object(author_doc, rel \\ "avatar") do
|
|||
|
||||
@spec gather_user_info(String.t()) :: {:ok, map()} | {:error, any()}
|
||||
def gather_user_info(username) do
|
||||
with {:ok, webfinger_data} <- WebFinger.finger(username),
|
||||
{:ok, feed_data} <- Websub.gather_feed_data(webfinger_data["topic"]) do
|
||||
with {:ok, webfinger_data} <- WebFinger.finger(username) do
|
||||
data =
|
||||
webfinger_data
|
||||
|> Map.merge(feed_data)
|
||||
|> Map.put("fqn", username)
|
||||
|
||||
{:ok, data}
|
||||
|
|
|
@ -509,9 +509,6 @@ defmodule Pleroma.Web.Router do
|
|||
get("/users/:nickname", Feed.FeedController, :feed_redirect)
|
||||
|
||||
post("/users/:nickname/salmon", OStatus.OStatusController, :salmon_incoming)
|
||||
post("/push/hub/:nickname", Websub.WebsubController, :websub_subscription_request)
|
||||
get("/push/subscriptions/:id", Websub.WebsubController, :websub_subscription_confirmation)
|
||||
post("/push/subscriptions/:id", Websub.WebsubController, :websub_incoming)
|
||||
|
||||
get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)
|
||||
end
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
<title><%= @user.nickname <> "'s timeline" %></title>
|
||||
<updated><%= most_recent_update(@activities, @user) %></updated>
|
||||
<logo><%= logo(@user) %></logo>
|
||||
<link rel="hub" href="<%= websub_url(@conn, :websub_subscription_request, @user.nickname) %>"/>
|
||||
<link rel="salmon" href="<%= o_status_url(@conn, :salmon_incoming, @user.nickname) %>"/>
|
||||
<link rel="self" href="<%= '#{feed_url(@conn, :feed, @user.nickname)}.atom' %>" type="application/atom+xml"/>
|
||||
|
||||
|
|
|
@ -1,332 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Websub do
|
||||
alias Ecto.Changeset
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.HTTP
|
||||
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
|
||||
alias Pleroma.Web.Websub.WebsubClientSubscription
|
||||
alias Pleroma.Web.Websub.WebsubServerSubscription
|
||||
alias Pleroma.Web.XML
|
||||
require Logger
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
@behaviour Pleroma.Web.Federator.Publisher
|
||||
|
||||
def verify(subscription, getter \\ &HTTP.get/3) do
|
||||
challenge = Base.encode16(:crypto.strong_rand_bytes(8))
|
||||
lease_seconds = NaiveDateTime.diff(subscription.valid_until, subscription.updated_at)
|
||||
lease_seconds = lease_seconds |> to_string
|
||||
|
||||
params = %{
|
||||
"hub.challenge": challenge,
|
||||
"hub.lease_seconds": lease_seconds,
|
||||
"hub.topic": subscription.topic,
|
||||
"hub.mode": "subscribe"
|
||||
}
|
||||
|
||||
url = hd(String.split(subscription.callback, "?"))
|
||||
query = URI.parse(subscription.callback).query || ""
|
||||
params = Map.merge(params, URI.decode_query(query))
|
||||
|
||||
with {:ok, response} <- getter.(url, [], params: params),
|
||||
^challenge <- response.body do
|
||||
changeset = Changeset.change(subscription, %{state: "active"})
|
||||
Repo.update(changeset)
|
||||
else
|
||||
e ->
|
||||
Logger.debug("Couldn't verify subscription")
|
||||
Logger.debug(inspect(e))
|
||||
{:error, subscription}
|
||||
end
|
||||
end
|
||||
|
||||
@supported_activities [
|
||||
"Create",
|
||||
"Follow",
|
||||
"Like",
|
||||
"Announce",
|
||||
"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 =
|
||||
user
|
||||
|> FeedRepresenter.to_simple_form([activity], [user])
|
||||
|> :xmerl.export_simple(:xmerl_xml)
|
||||
|> to_string
|
||||
|
||||
query =
|
||||
from(
|
||||
sub in WebsubServerSubscription,
|
||||
where: sub.topic == ^topic and sub.state == "active",
|
||||
where: fragment("? > (NOW() at time zone 'UTC')", sub.valid_until)
|
||||
)
|
||||
|
||||
subscriptions = Repo.all(query)
|
||||
|
||||
callbacks = Enum.map(subscriptions, & &1.callback)
|
||||
reachable_callbacks_metadata = Instances.filter_reachable(callbacks)
|
||||
reachable_callbacks = Map.keys(reachable_callbacks_metadata)
|
||||
|
||||
subscriptions
|
||||
|> Enum.filter(&(&1.callback in reachable_callbacks))
|
||||
|> Enum.each(fn sub ->
|
||||
data = %{
|
||||
xml: response,
|
||||
topic: topic,
|
||||
callback: sub.callback,
|
||||
secret: sub.secret,
|
||||
unreachable_since: reachable_callbacks_metadata[sub.callback]
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
def incoming_subscription_request(user, %{"hub.mode" => "subscribe"} = params) do
|
||||
with {:ok, topic} <- valid_topic(params, user),
|
||||
{:ok, lease_time} <- lease_time(params),
|
||||
secret <- params["hub.secret"],
|
||||
callback <- params["hub.callback"] do
|
||||
subscription = get_subscription(topic, callback)
|
||||
|
||||
data = %{
|
||||
state: subscription.state || "requested",
|
||||
topic: topic,
|
||||
secret: secret,
|
||||
callback: callback
|
||||
}
|
||||
|
||||
change = Changeset.change(subscription, data)
|
||||
websub = Repo.insert_or_update!(change)
|
||||
|
||||
change =
|
||||
Changeset.change(websub, %{valid_until: NaiveDateTime.add(websub.updated_at, lease_time)})
|
||||
|
||||
websub = Repo.update!(change)
|
||||
|
||||
Federator.verify_websub(websub)
|
||||
|
||||
{:ok, websub}
|
||||
else
|
||||
{:error, reason} ->
|
||||
Logger.debug("Couldn't create subscription")
|
||||
Logger.debug(inspect(reason))
|
||||
|
||||
{:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
def incoming_subscription_request(user, params) do
|
||||
Logger.info("Unhandled WebSub request for #{user.nickname}: #{inspect(params)}")
|
||||
|
||||
{:error, "Invalid WebSub request"}
|
||||
end
|
||||
|
||||
defp get_subscription(topic, callback) do
|
||||
Repo.get_by(WebsubServerSubscription, topic: topic, callback: callback) ||
|
||||
%WebsubServerSubscription{}
|
||||
end
|
||||
|
||||
# Temp hack for mastodon.
|
||||
defp lease_time(%{"hub.lease_seconds" => ""}) do
|
||||
# three days
|
||||
{:ok, 60 * 60 * 24 * 3}
|
||||
end
|
||||
|
||||
defp lease_time(%{"hub.lease_seconds" => lease_seconds}) do
|
||||
{:ok, String.to_integer(lease_seconds)}
|
||||
end
|
||||
|
||||
defp lease_time(_) do
|
||||
# three days
|
||||
{:ok, 60 * 60 * 24 * 3}
|
||||
end
|
||||
|
||||
defp valid_topic(%{"hub.topic" => topic}, user) do
|
||||
if topic == OStatus.feed_path(user) do
|
||||
{:ok, OStatus.feed_path(user)}
|
||||
else
|
||||
{:error, "Wrong topic requested, expected #{OStatus.feed_path(user)}, got #{topic}"}
|
||||
end
|
||||
end
|
||||
|
||||
def subscribe(subscriber, subscribed, requester \\ &request_subscription/1) do
|
||||
topic = subscribed.info.topic
|
||||
# FIXME: Race condition, use transactions
|
||||
{:ok, subscription} =
|
||||
with subscription when not is_nil(subscription) <-
|
||||
Repo.get_by(WebsubClientSubscription, topic: topic) do
|
||||
subscribers = [subscriber.ap_id | subscription.subscribers] |> Enum.uniq()
|
||||
change = Ecto.Changeset.change(subscription, %{subscribers: subscribers})
|
||||
Repo.update(change)
|
||||
else
|
||||
_e ->
|
||||
subscription = %WebsubClientSubscription{
|
||||
topic: topic,
|
||||
hub: subscribed.info.hub,
|
||||
subscribers: [subscriber.ap_id],
|
||||
state: "requested",
|
||||
secret: :crypto.strong_rand_bytes(8) |> Base.url_encode64(),
|
||||
user: subscribed
|
||||
}
|
||||
|
||||
Repo.insert(subscription)
|
||||
end
|
||||
|
||||
requester.(subscription)
|
||||
end
|
||||
|
||||
def gather_feed_data(topic, getter \\ &HTTP.get/1) do
|
||||
with {:ok, response} <- getter.(topic),
|
||||
status when status in 200..299 <- response.status,
|
||||
body <- response.body,
|
||||
doc <- XML.parse_document(body),
|
||||
uri when not is_nil(uri) <- XML.string_from_xpath("/feed/author[1]/uri", doc),
|
||||
hub when not is_nil(hub) <- XML.string_from_xpath(~S{/feed/link[@rel="hub"]/@href}, doc) do
|
||||
name = XML.string_from_xpath("/feed/author[1]/name", doc)
|
||||
preferred_username = XML.string_from_xpath("/feed/author[1]/poco:preferredUsername", doc)
|
||||
display_name = XML.string_from_xpath("/feed/author[1]/poco:displayName", doc)
|
||||
avatar = OStatus.make_avatar_object(doc)
|
||||
bio = XML.string_from_xpath("/feed/author[1]/summary", doc)
|
||||
|
||||
{:ok,
|
||||
%{
|
||||
"uri" => uri,
|
||||
"hub" => hub,
|
||||
"nickname" => preferred_username || name,
|
||||
"name" => display_name || name,
|
||||
"host" => URI.parse(uri).host,
|
||||
"avatar" => avatar,
|
||||
"bio" => bio
|
||||
}}
|
||||
else
|
||||
e ->
|
||||
{:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
def request_subscription(websub, poster \\ &HTTP.post/3, timeout \\ 10_000) do
|
||||
data = [
|
||||
"hub.mode": "subscribe",
|
||||
"hub.topic": websub.topic,
|
||||
"hub.secret": websub.secret,
|
||||
"hub.callback": Helpers.websub_url(Endpoint, :websub_subscription_confirmation, websub.id)
|
||||
]
|
||||
|
||||
# This checks once a second if we are confirmed yet
|
||||
websub_checker = fn ->
|
||||
helper = fn helper ->
|
||||
:timer.sleep(1000)
|
||||
websub = Repo.get_by(WebsubClientSubscription, id: websub.id, state: "accepted")
|
||||
if websub, do: websub, else: helper.(helper)
|
||||
end
|
||||
|
||||
helper.(helper)
|
||||
end
|
||||
|
||||
task = Task.async(websub_checker)
|
||||
|
||||
with {:ok, %{status: 202}} <-
|
||||
poster.(websub.hub, {:form, data}, "Content-type": "application/x-www-form-urlencoded"),
|
||||
{:ok, websub} <- Task.yield(task, timeout) do
|
||||
{:ok, websub}
|
||||
else
|
||||
e ->
|
||||
Task.shutdown(task)
|
||||
|
||||
change = Ecto.Changeset.change(websub, %{state: "rejected"})
|
||||
{:ok, websub} = Repo.update(change)
|
||||
|
||||
Logger.debug(fn -> "Couldn't confirm subscription: #{inspect(websub)}" end)
|
||||
Logger.debug(fn -> "error: #{inspect(e)}" end)
|
||||
|
||||
{:error, websub}
|
||||
end
|
||||
end
|
||||
|
||||
def refresh_subscriptions(delta \\ 60 * 60 * 24) do
|
||||
Logger.debug("Refreshing subscriptions")
|
||||
|
||||
cut_off = NaiveDateTime.add(NaiveDateTime.utc_now(), delta)
|
||||
|
||||
query = from(sub in WebsubClientSubscription, where: sub.valid_until < ^cut_off)
|
||||
|
||||
subs = Repo.all(query)
|
||||
|
||||
Enum.each(subs, fn sub ->
|
||||
Federator.request_subscription(sub)
|
||||
end)
|
||||
end
|
||||
|
||||
def publish_one(%{xml: xml, topic: topic, callback: callback, secret: secret} = params) do
|
||||
signature = sign(secret || "", xml)
|
||||
Logger.info(fn -> "Pushing #{topic} to #{callback}" end)
|
||||
|
||||
with {:ok, %{status: code}} when code in 200..299 <-
|
||||
HTTP.post(
|
||||
callback,
|
||||
xml,
|
||||
[
|
||||
{"Content-Type", "application/atom+xml"},
|
||||
{"X-Hub-Signature", "sha1=#{signature}"}
|
||||
]
|
||||
) do
|
||||
if !Map.has_key?(params, :unreachable_since) || params[:unreachable_since],
|
||||
do: Instances.set_reachable(callback)
|
||||
|
||||
Logger.info(fn -> "Pushed to #{callback}, code #{code}" end)
|
||||
{:ok, code}
|
||||
else
|
||||
{_post_result, response} ->
|
||||
unless params[:unreachable_since], do: Instances.set_reachable(callback)
|
||||
Logger.debug(fn -> "Couldn't push to #{callback}, #{inspect(response)}" end)
|
||||
{: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
|
|
@ -1,20 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Websub.WebsubClientSubscription do
|
||||
use Ecto.Schema
|
||||
alias Pleroma.User
|
||||
|
||||
schema "websub_client_subscriptions" do
|
||||
field(:topic, :string)
|
||||
field(:secret, :string)
|
||||
field(:valid_until, :naive_datetime_usec)
|
||||
field(:state, :string)
|
||||
field(:subscribers, {:array, :string}, default: [])
|
||||
field(:hub, :string)
|
||||
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
end
|
|
@ -1,99 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Websub.WebsubController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.Federator
|
||||
alias Pleroma.Web.Websub
|
||||
alias Pleroma.Web.Websub.WebsubClientSubscription
|
||||
|
||||
require Logger
|
||||
|
||||
plug(
|
||||
Pleroma.Web.FederatingPlug
|
||||
when action in [
|
||||
:websub_subscription_request,
|
||||
:websub_subscription_confirmation,
|
||||
:websub_incoming
|
||||
]
|
||||
)
|
||||
|
||||
def websub_subscription_request(conn, %{"nickname" => nickname} = params) do
|
||||
user = User.get_cached_by_nickname(nickname)
|
||||
|
||||
with {:ok, _websub} <- Websub.incoming_subscription_request(user, params) do
|
||||
conn
|
||||
|> send_resp(202, "Accepted")
|
||||
else
|
||||
{:error, reason} ->
|
||||
conn
|
||||
|> send_resp(500, reason)
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: Extract this into the Websub module
|
||||
def websub_subscription_confirmation(
|
||||
conn,
|
||||
%{
|
||||
"id" => id,
|
||||
"hub.mode" => "subscribe",
|
||||
"hub.challenge" => challenge,
|
||||
"hub.topic" => topic
|
||||
} = params
|
||||
) do
|
||||
Logger.debug("Got WebSub confirmation")
|
||||
Logger.debug(inspect(params))
|
||||
|
||||
lease_seconds =
|
||||
if params["hub.lease_seconds"] do
|
||||
String.to_integer(params["hub.lease_seconds"])
|
||||
else
|
||||
# Guess 3 days
|
||||
60 * 60 * 24 * 3
|
||||
end
|
||||
|
||||
with %WebsubClientSubscription{} = websub <-
|
||||
Repo.get_by(WebsubClientSubscription, id: id, topic: topic) do
|
||||
valid_until = NaiveDateTime.add(NaiveDateTime.utc_now(), lease_seconds)
|
||||
change = Ecto.Changeset.change(websub, %{state: "accepted", valid_until: valid_until})
|
||||
{:ok, _websub} = Repo.update(change)
|
||||
|
||||
conn
|
||||
|> send_resp(200, challenge)
|
||||
else
|
||||
_e ->
|
||||
conn
|
||||
|> send_resp(500, "Error")
|
||||
end
|
||||
end
|
||||
|
||||
def websub_subscription_confirmation(conn, params) do
|
||||
Logger.info("Invalid WebSub confirmation request: #{inspect(params)}")
|
||||
|
||||
conn
|
||||
|> send_resp(500, "Invalid parameters")
|
||||
end
|
||||
|
||||
def websub_incoming(conn, %{"id" => id}) do
|
||||
with "sha1=" <> signature <- hd(get_req_header(conn, "x-hub-signature")),
|
||||
signature <- String.downcase(signature),
|
||||
%WebsubClientSubscription{} = websub <- Repo.get(WebsubClientSubscription, id),
|
||||
{:ok, body, _conn} = read_body(conn),
|
||||
^signature <- Websub.sign(websub.secret, body) do
|
||||
Federator.incoming_doc(body)
|
||||
|
||||
conn
|
||||
|> send_resp(200, "OK")
|
||||
else
|
||||
_e ->
|
||||
Logger.debug("Can't handle incoming subscription post")
|
||||
|
||||
conn
|
||||
|> send_resp(500, "Error")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,17 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Websub.WebsubServerSubscription do
|
||||
use Ecto.Schema
|
||||
|
||||
schema "websub_server_subscriptions" do
|
||||
field(:topic, :string)
|
||||
field(:callback, :string)
|
||||
field(:secret, :string)
|
||||
field(:valid_until, :naive_datetime)
|
||||
field(:state, :string)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
end
|
|
@ -281,26 +281,6 @@ def follow_activity_factory do
|
|||
}
|
||||
end
|
||||
|
||||
def websub_subscription_factory do
|
||||
%Pleroma.Web.Websub.WebsubServerSubscription{
|
||||
topic: "http://example.org",
|
||||
callback: "http://example.org/callback",
|
||||
secret: "here's a secret",
|
||||
valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 100),
|
||||
state: "requested"
|
||||
}
|
||||
end
|
||||
|
||||
def websub_client_subscription_factory do
|
||||
%Pleroma.Web.Websub.WebsubClientSubscription{
|
||||
topic: "http://example.org",
|
||||
secret: "here's a secret",
|
||||
valid_until: nil,
|
||||
state: "requested",
|
||||
subscribers: []
|
||||
}
|
||||
end
|
||||
|
||||
def oauth_app_factory do
|
||||
%Pleroma.Web.OAuth.App{
|
||||
client_name: "Some client",
|
||||
|
|
|
@ -190,23 +190,6 @@ test "local users do not automatically follow local locked accounts" do
|
|||
refute User.following?(follower, followed)
|
||||
end
|
||||
|
||||
# This is a somewhat useless test.
|
||||
# test "following a remote user will ensure a websub subscription is present" do
|
||||
# user = insert(:user)
|
||||
# {:ok, followed} = OStatus.make_user("shp@social.heldscal.la")
|
||||
|
||||
# assert followed.local == false
|
||||
|
||||
# {:ok, user} = User.follow(user, followed)
|
||||
# assert User.ap_followers(followed) in user.following
|
||||
|
||||
# query = from w in WebsubClientSubscription,
|
||||
# where: w.topic == ^followed.info["topic"]
|
||||
# websub = Repo.one(query)
|
||||
|
||||
# assert websub
|
||||
# end
|
||||
|
||||
describe "unfollow/2" do
|
||||
setup do
|
||||
setting = Pleroma.Config.get([:instance, :external_user_synchronization])
|
||||
|
|
|
@ -14,7 +14,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.OStatus
|
||||
alias Pleroma.Web.Websub.WebsubClientSubscription
|
||||
|
||||
import Mock
|
||||
import Pleroma.Factory
|
||||
|
@ -1371,21 +1370,6 @@ test "it upgrades a user to activitypub" do
|
|||
end
|
||||
end
|
||||
|
||||
describe "maybe_retire_websub" do
|
||||
test "it deletes all websub client subscripitions with the user as topic" do
|
||||
subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/rye.atom"}
|
||||
{:ok, ws} = Repo.insert(subscription)
|
||||
|
||||
subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/pasty.atom"}
|
||||
{:ok, ws2} = Repo.insert(subscription)
|
||||
|
||||
Transmogrifier.maybe_retire_websub("https://niu.moe/users/rye")
|
||||
|
||||
refute Repo.get(WebsubClientSubscription, ws.id)
|
||||
assert Repo.get(WebsubClientSubscription, ws2.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe "actor rewriting" do
|
||||
test "it fixes the actor URL property to be a proper URI" do
|
||||
data = %{
|
||||
|
|
|
@ -64,19 +64,6 @@ test "handle incoming notes - GS, subscription" do
|
|||
assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
|
||||
end
|
||||
|
||||
test "handle incoming notes with attachments - GS, subscription" do
|
||||
incoming = File.read!("test/fixtures/incoming_websub_gnusocial_attachments.xml")
|
||||
{:ok, [activity]} = OStatus.handle_incoming(incoming)
|
||||
object = Object.normalize(activity)
|
||||
|
||||
assert activity.data["type"] == "Create"
|
||||
assert object.data["type"] == "Note"
|
||||
assert object.data["actor"] == "https://social.heldscal.la/user/23211"
|
||||
assert object.data["attachment"] |> length == 2
|
||||
assert object.data["external_url"] == "https://social.heldscal.la/notice/2020923"
|
||||
assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
|
||||
end
|
||||
|
||||
test "handle incoming notes with tags" do
|
||||
incoming = File.read!("test/fixtures/ostatus_incoming_post_tag.xml")
|
||||
{:ok, [activity]} = OStatus.handle_incoming(incoming)
|
||||
|
@ -221,29 +208,6 @@ test "handle incoming retweets - Mastodon, salmon" do
|
|||
refute String.contains?(retweeted_object.data["content"], "Test account")
|
||||
end
|
||||
|
||||
test "handle incoming favorites - GS, websub" do
|
||||
capture_log(fn ->
|
||||
incoming = File.read!("test/fixtures/favorite.xml")
|
||||
{:ok, [[activity, favorited_activity]]} = OStatus.handle_incoming(incoming)
|
||||
|
||||
assert activity.data["type"] == "Like"
|
||||
assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
|
||||
assert activity.data["object"] == favorited_activity.data["object"]
|
||||
|
||||
assert activity.data["id"] ==
|
||||
"tag:social.heldscal.la,2017-05-05:fave:23211:comment:2061643:2017-05-05T09:12:50+00:00"
|
||||
|
||||
refute activity.local
|
||||
assert favorited_activity.data["type"] == "Create"
|
||||
assert favorited_activity.data["actor"] == "https://shitposter.club/user/1"
|
||||
|
||||
assert favorited_activity.data["object"] ==
|
||||
"tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
|
||||
|
||||
refute favorited_activity.local
|
||||
end)
|
||||
end
|
||||
|
||||
test "handle conversation references" do
|
||||
incoming = File.read!("test/fixtures/mastodon_conversation.xml")
|
||||
{:ok, [activity]} = OStatus.handle_incoming(incoming)
|
||||
|
@ -252,24 +216,6 @@ test "handle conversation references" do
|
|||
"tag:mastodon.social,2017-08-28:objectId=7876885:objectType=Conversation"
|
||||
end
|
||||
|
||||
test "handle incoming favorites with locally available object - GS, websub" do
|
||||
note_activity = insert(:note_activity)
|
||||
object = Object.normalize(note_activity)
|
||||
|
||||
incoming =
|
||||
File.read!("test/fixtures/favorite_with_local_note.xml")
|
||||
|> String.replace("localid", object.data["id"])
|
||||
|
||||
{:ok, [[activity, favorited_activity]]} = OStatus.handle_incoming(incoming)
|
||||
|
||||
assert activity.data["type"] == "Like"
|
||||
assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
|
||||
assert activity.data["object"] == object.data["id"]
|
||||
refute activity.local
|
||||
assert note_activity.id == favorited_activity.id
|
||||
assert favorited_activity.local
|
||||
end
|
||||
|
||||
test_with_mock "handle incoming replies, fetching replied-to activities if we don't have them",
|
||||
OStatus,
|
||||
[:passthrough],
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Websub.WebsubControllerTest do
|
||||
use Pleroma.Web.ConnCase
|
||||
import Pleroma.Factory
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Web.Websub
|
||||
alias Pleroma.Web.Websub.WebsubClientSubscription
|
||||
|
||||
clear_config_all([:instance, :federating]) do
|
||||
Pleroma.Config.put([:instance, :federating], true)
|
||||
end
|
||||
|
||||
test "websub subscription request", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
path = Pleroma.Web.OStatus.pubsub_path(user)
|
||||
|
||||
data = %{
|
||||
"hub.callback": "http://example.org/sub",
|
||||
"hub.mode": "subscribe",
|
||||
"hub.topic": Pleroma.Web.OStatus.feed_path(user),
|
||||
"hub.secret": "a random secret",
|
||||
"hub.lease_seconds": "100"
|
||||
}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> post(path, data)
|
||||
|
||||
assert response(conn, 202) == "Accepted"
|
||||
end
|
||||
|
||||
test "websub subscription confirmation", %{conn: conn} do
|
||||
websub = insert(:websub_client_subscription)
|
||||
|
||||
params = %{
|
||||
"hub.mode" => "subscribe",
|
||||
"hub.topic" => websub.topic,
|
||||
"hub.challenge" => "some challenge",
|
||||
"hub.lease_seconds" => "100"
|
||||
}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> get("/push/subscriptions/#{websub.id}", params)
|
||||
|
||||
websub = Repo.get(WebsubClientSubscription, websub.id)
|
||||
|
||||
assert response(conn, 200) == "some challenge"
|
||||
assert websub.state == "accepted"
|
||||
assert_in_delta NaiveDateTime.diff(websub.valid_until, NaiveDateTime.utc_now()), 100, 5
|
||||
end
|
||||
|
||||
describe "websub_incoming" do
|
||||
test "accepts incoming feed updates", %{conn: conn} do
|
||||
websub = insert(:websub_client_subscription)
|
||||
doc = "some stuff"
|
||||
signature = Websub.sign(websub.secret, doc)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("x-hub-signature", "sha1=" <> signature)
|
||||
|> put_req_header("content-type", "application/atom+xml")
|
||||
|> post("/push/subscriptions/#{websub.id}", doc)
|
||||
|
||||
assert response(conn, 200) == "OK"
|
||||
end
|
||||
|
||||
test "rejects incoming feed updates with the wrong signature", %{conn: conn} do
|
||||
websub = insert(:websub_client_subscription)
|
||||
doc = "some stuff"
|
||||
signature = Websub.sign("wrong secret", doc)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("x-hub-signature", "sha1=" <> signature)
|
||||
|> put_req_header("content-type", "application/atom+xml")
|
||||
|> post("/push/subscriptions/#{websub.id}", doc)
|
||||
|
||||
assert response(conn, 500) == "Error"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,236 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.WebsubTest do
|
||||
use Pleroma.DataCase
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
|
||||
alias Pleroma.Tests.ObanHelpers
|
||||
alias Pleroma.Web.Router.Helpers
|
||||
alias Pleroma.Web.Websub
|
||||
alias Pleroma.Web.Websub.WebsubClientSubscription
|
||||
alias Pleroma.Web.Websub.WebsubServerSubscription
|
||||
alias Pleroma.Workers.SubscriberWorker
|
||||
|
||||
import Pleroma.Factory
|
||||
import Tesla.Mock
|
||||
|
||||
setup do
|
||||
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||
:ok
|
||||
end
|
||||
|
||||
test "a verification of a request that is accepted" do
|
||||
sub = insert(:websub_subscription)
|
||||
topic = sub.topic
|
||||
|
||||
getter = fn _path, _headers, options ->
|
||||
%{
|
||||
"hub.challenge": challenge,
|
||||
"hub.lease_seconds": seconds,
|
||||
"hub.topic": ^topic,
|
||||
"hub.mode": "subscribe"
|
||||
} = Keyword.get(options, :params)
|
||||
|
||||
assert String.to_integer(seconds) > 0
|
||||
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: challenge
|
||||
}}
|
||||
end
|
||||
|
||||
{:ok, sub} = Websub.verify(sub, getter)
|
||||
assert sub.state == "active"
|
||||
end
|
||||
|
||||
test "a verification of a request that doesn't return 200" do
|
||||
sub = insert(:websub_subscription)
|
||||
|
||||
getter = fn _path, _headers, _options ->
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 500,
|
||||
body: ""
|
||||
}}
|
||||
end
|
||||
|
||||
{:error, sub} = Websub.verify(sub, getter)
|
||||
# Keep the current state.
|
||||
assert sub.state == "requested"
|
||||
end
|
||||
|
||||
test "an incoming subscription request" do
|
||||
user = insert(:user)
|
||||
|
||||
data = %{
|
||||
"hub.callback" => "http://example.org/sub",
|
||||
"hub.mode" => "subscribe",
|
||||
"hub.topic" => Pleroma.Web.OStatus.feed_path(user),
|
||||
"hub.secret" => "a random secret",
|
||||
"hub.lease_seconds" => "100"
|
||||
}
|
||||
|
||||
{:ok, subscription} = Websub.incoming_subscription_request(user, data)
|
||||
assert subscription.topic == Pleroma.Web.OStatus.feed_path(user)
|
||||
assert subscription.state == "requested"
|
||||
assert subscription.secret == "a random secret"
|
||||
assert subscription.callback == "http://example.org/sub"
|
||||
end
|
||||
|
||||
test "an incoming subscription request for an existing subscription" do
|
||||
user = insert(:user)
|
||||
|
||||
sub =
|
||||
insert(:websub_subscription, state: "accepted", topic: Pleroma.Web.OStatus.feed_path(user))
|
||||
|
||||
data = %{
|
||||
"hub.callback" => sub.callback,
|
||||
"hub.mode" => "subscribe",
|
||||
"hub.topic" => Pleroma.Web.OStatus.feed_path(user),
|
||||
"hub.secret" => "a random secret",
|
||||
"hub.lease_seconds" => "100"
|
||||
}
|
||||
|
||||
{:ok, subscription} = Websub.incoming_subscription_request(user, data)
|
||||
assert subscription.topic == Pleroma.Web.OStatus.feed_path(user)
|
||||
assert subscription.state == sub.state
|
||||
assert subscription.secret == "a random secret"
|
||||
assert subscription.callback == sub.callback
|
||||
assert length(Repo.all(WebsubServerSubscription)) == 1
|
||||
assert subscription.id == sub.id
|
||||
end
|
||||
|
||||
def accepting_verifier(subscription) do
|
||||
{:ok, %{subscription | state: "accepted"}}
|
||||
end
|
||||
|
||||
test "initiate a subscription for a given user and topic" do
|
||||
subscriber = insert(:user)
|
||||
user = insert(:user, %{info: %Pleroma.User.Info{topic: "some_topic", hub: "some_hub"}})
|
||||
|
||||
{:ok, websub} = Websub.subscribe(subscriber, user, &accepting_verifier/1)
|
||||
assert websub.subscribers == [subscriber.ap_id]
|
||||
assert websub.topic == "some_topic"
|
||||
assert websub.hub == "some_hub"
|
||||
assert is_binary(websub.secret)
|
||||
assert websub.user == user
|
||||
assert websub.state == "accepted"
|
||||
end
|
||||
|
||||
test "discovers the hub and canonical url" do
|
||||
topic = "https://mastodon.social/users/lambadalambda.atom"
|
||||
|
||||
{:ok, discovered} = Websub.gather_feed_data(topic)
|
||||
|
||||
expected = %{
|
||||
"hub" => "https://mastodon.social/api/push",
|
||||
"uri" => "https://mastodon.social/users/lambadalambda",
|
||||
"nickname" => "lambadalambda",
|
||||
"name" => "Critical Value",
|
||||
"host" => "mastodon.social",
|
||||
"bio" => "a cool dude.",
|
||||
"avatar" => %{
|
||||
"type" => "Image",
|
||||
"url" => [
|
||||
%{
|
||||
"href" =>
|
||||
"https://files.mastodon.social/accounts/avatars/000/000/264/original/1429214160519.gif?1492379244",
|
||||
"mediaType" => "image/gif",
|
||||
"type" => "Link"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
assert expected == discovered
|
||||
end
|
||||
|
||||
test "calls the hub, requests topic" do
|
||||
hub = "https://social.heldscal.la/main/push/hub"
|
||||
topic = "https://social.heldscal.la/api/statuses/user_timeline/23211.atom"
|
||||
websub = insert(:websub_client_subscription, %{hub: hub, topic: topic})
|
||||
|
||||
poster = fn ^hub, {:form, data}, _headers ->
|
||||
assert Keyword.get(data, :"hub.mode") == "subscribe"
|
||||
|
||||
assert Keyword.get(data, :"hub.callback") ==
|
||||
Helpers.websub_url(
|
||||
Pleroma.Web.Endpoint,
|
||||
:websub_subscription_confirmation,
|
||||
websub.id
|
||||
)
|
||||
|
||||
{:ok, %{status: 202}}
|
||||
end
|
||||
|
||||
task = Task.async(fn -> Websub.request_subscription(websub, poster) end)
|
||||
|
||||
change = Ecto.Changeset.change(websub, %{state: "accepted"})
|
||||
{:ok, _} = Repo.update(change)
|
||||
|
||||
{:ok, websub} = Task.await(task)
|
||||
|
||||
assert websub.state == "accepted"
|
||||
end
|
||||
|
||||
test "rejects the subscription if it can't be accepted" do
|
||||
hub = "https://social.heldscal.la/main/push/hub"
|
||||
topic = "https://social.heldscal.la/api/statuses/user_timeline/23211.atom"
|
||||
websub = insert(:websub_client_subscription, %{hub: hub, topic: topic})
|
||||
|
||||
poster = fn ^hub, {:form, _data}, _headers ->
|
||||
{:ok, %{status: 202}}
|
||||
end
|
||||
|
||||
{:error, websub} = Websub.request_subscription(websub, poster, 1000)
|
||||
assert websub.state == "rejected"
|
||||
|
||||
websub = insert(:websub_client_subscription, %{hub: hub, topic: topic})
|
||||
|
||||
poster = fn ^hub, {:form, _data}, _headers ->
|
||||
{:ok, %{status: 400}}
|
||||
end
|
||||
|
||||
{:error, websub} = Websub.request_subscription(websub, poster, 1000)
|
||||
assert websub.state == "rejected"
|
||||
end
|
||||
|
||||
test "sign a text" do
|
||||
signed = Websub.sign("secret", "text")
|
||||
assert signed == "B8392C23690CCF871F37EC270BE1582DEC57A503" |> String.downcase()
|
||||
|
||||
_signed = Websub.sign("secret", [["て"], ['す']])
|
||||
end
|
||||
|
||||
describe "renewing subscriptions" do
|
||||
test "it renews subscriptions that have less than a day of time left" do
|
||||
day = 60 * 60 * 24
|
||||
now = NaiveDateTime.utc_now()
|
||||
|
||||
still_good =
|
||||
insert(:websub_client_subscription, %{
|
||||
valid_until: NaiveDateTime.add(now, 2 * day),
|
||||
topic: "http://example.org/still_good",
|
||||
hub: "http://example.org/still_good",
|
||||
state: "accepted"
|
||||
})
|
||||
|
||||
needs_refresh =
|
||||
insert(:websub_client_subscription, %{
|
||||
valid_until: NaiveDateTime.add(now, day - 100),
|
||||
topic: "http://example.org/needs_refresh",
|
||||
hub: "http://example.org/needs_refresh",
|
||||
state: "accepted"
|
||||
})
|
||||
|
||||
_refresh = Websub.refresh_subscriptions()
|
||||
ObanHelpers.perform(all_enqueued(worker: SubscriberWorker))
|
||||
|
||||
assert still_good == Repo.get(WebsubClientSubscription, still_good.id)
|
||||
refute needs_refresh == Repo.get(WebsubClientSubscription, needs_refresh.id)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue