aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorlambda <pleromagit@rogerbraun.net>2018-08-27 08:29:25 +0000
committerlambda <pleromagit@rogerbraun.net>2018-08-27 08:29:25 +0000
commit46c7c2380c9f923b6c9e1521b025fd45aed0ae37 (patch)
treeb68eb17204a86f61dba574ad7376a4891dd262b6 /lib
parent440b459cd14778e155cd6a3550847b1277fbd1f1 (diff)
parent0f5bff8c66fa2b67633fe05de8aaa1985f4d98f8 (diff)
downloadpleroma-46c7c2380c9f923b6c9e1521b025fd45aed0ae37.tar.gz
Merge branch 'feature/relay' into 'develop'
message relay Closes #144 See merge request pleroma/pleroma!264
Diffstat (limited to 'lib')
-rw-r--r--lib/mix/tasks/relay_follow.ex15
-rw-r--r--lib/mix/tasks/relay_unfollow.ex15
-rw-r--r--lib/pleroma/user.ex30
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex13
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub_controller.ex12
-rw-r--r--lib/pleroma/web/activity_pub/relay.ex44
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex27
-rw-r--r--lib/pleroma/web/activity_pub/views/user_view.ex29
-rw-r--r--lib/pleroma/web/federator/federator.ex6
-rw-r--r--lib/pleroma/web/router.ex12
10 files changed, 198 insertions, 5 deletions
diff --git a/lib/mix/tasks/relay_follow.ex b/lib/mix/tasks/relay_follow.ex
new file mode 100644
index 000000000..ac6f20924
--- /dev/null
+++ b/lib/mix/tasks/relay_follow.ex
@@ -0,0 +1,15 @@
+defmodule Mix.Tasks.RelayFollow do
+ use Mix.Task
+ require Logger
+ alias Pleroma.Web.ActivityPub.Relay
+
+ @shortdoc "Follows a remote relay"
+ def run([target]) do
+ Mix.Task.run("app.start")
+
+ :ok = Relay.follow(target)
+
+ # put this task to sleep to allow the genserver to push out the messages
+ :timer.sleep(500)
+ end
+end
diff --git a/lib/mix/tasks/relay_unfollow.ex b/lib/mix/tasks/relay_unfollow.ex
new file mode 100644
index 000000000..4621ace83
--- /dev/null
+++ b/lib/mix/tasks/relay_unfollow.ex
@@ -0,0 +1,15 @@
+defmodule Mix.Tasks.RelayUnfollow do
+ use Mix.Task
+ require Logger
+ alias Pleroma.Web.ActivityPub.Relay
+
+ @shortdoc "Follows a remote relay"
+ def run([target]) do
+ Mix.Task.run("app.start")
+
+ :ok = Relay.unfollow(target)
+
+ # put this task to sleep to allow the genserver to push out the messages
+ :timer.sleep(500)
+ end
+end
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 7d7f3b23e..88293a4f3 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -77,7 +77,7 @@ defmodule Pleroma.User do
changes =
%User{}
|> cast(params, [:bio, :name, :ap_id, :nickname, :info, :avatar])
- |> validate_required([:name, :ap_id, :nickname])
+ |> validate_required([:name, :ap_id])
|> unique_constraint(:nickname)
|> validate_format(:nickname, @email_regex)
|> validate_length(:bio, max: 5000)
@@ -516,7 +516,8 @@ defmodule Pleroma.User do
u.nickname,
u.name
)
- }
+ },
+ where: not is_nil(u.nickname)
)
q =
@@ -595,7 +596,11 @@ defmodule Pleroma.User do
end
def local_user_query() do
- from(u in User, where: u.local == true)
+ from(
+ u in User,
+ where: u.local == true,
+ where: not is_nil(u.nickname)
+ )
end
def deactivate(%User{} = user) do
@@ -654,6 +659,25 @@ defmodule Pleroma.User do
end
end
+ def get_or_create_instance_user do
+ relay_uri = "#{Pleroma.Web.Endpoint.url()}/relay"
+
+ if user = get_by_ap_id(relay_uri) do
+ user
+ else
+ changes =
+ %User{}
+ |> cast(%{}, [:ap_id, :nickname, :local])
+ |> put_change(:ap_id, relay_uri)
+ |> put_change(:nickname, nil)
+ |> put_change(:local, true)
+ |> put_change(:follower_address, relay_uri <> "/followers")
+
+ {:ok, user} = Repo.insert(changes)
+ user
+ end
+ end
+
# AP style
def public_key_from_info(%{
"source_data" => %{"publicKey" => %{"publicKeyPem" => public_key_pem}}
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 298d7817a..68b398786 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -572,12 +572,23 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
"locked" => locked
},
avatar: avatar,
- nickname: "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}",
name: data["name"],
follower_address: data["followers"],
bio: data["summary"]
}
+ # nickname can be nil because of virtual actors
+ user_data =
+ if data["preferredUsername"] do
+ Map.put(
+ user_data,
+ :nickname,
+ "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
+ )
+ else
+ Map.put(user_data, :nickname, nil)
+ end
+
{:ok, user_data}
end
diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
index d337532d0..52b2a467e 100644
--- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
@@ -3,6 +3,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
alias Pleroma.{User, Object}
alias Pleroma.Web.ActivityPub.{ObjectView, UserView}
alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Pleroma.Web.ActivityPub.Relay
alias Pleroma.Web.Federator
require Logger
@@ -107,6 +108,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
json(conn, "ok")
end
+ def relay(conn, params) do
+ with %User{} = user <- Relay.get_actor(),
+ {:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
+ conn
+ |> put_resp_header("content-type", "application/activity+json")
+ |> json(UserView.render("user.json", %{user: user}))
+ else
+ nil -> {:error, :not_found}
+ end
+ end
+
def errors(conn, {:error, :not_found}) do
conn
|> put_status(404)
diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex
new file mode 100644
index 000000000..d30853d62
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/relay.ex
@@ -0,0 +1,44 @@
+defmodule Pleroma.Web.ActivityPub.Relay do
+ alias Pleroma.{User, Object, Activity}
+ alias Pleroma.Web.ActivityPub.ActivityPub
+ require Logger
+
+ def get_actor do
+ User.get_or_create_instance_user()
+ end
+
+ def follow(target_instance) do
+ with %User{} = local_user <- get_actor(),
+ %User{} = target_user <- User.get_or_fetch_by_ap_id(target_instance),
+ {:ok, activity} <- ActivityPub.follow(local_user, target_user) do
+ Logger.info("relay: followed instance: #{target_instance}; id=#{activity.data["id"]}")
+ else
+ e -> Logger.error("error: #{inspect(e)}")
+ end
+
+ :ok
+ end
+
+ def unfollow(target_instance) do
+ with %User{} = local_user <- get_actor(),
+ %User{} = target_user <- User.get_or_fetch_by_ap_id(target_instance),
+ {:ok, activity} <- ActivityPub.unfollow(local_user, target_user) do
+ Logger.info("relay: unfollowed instance: #{target_instance}: id=#{activity.data["id"]}")
+ else
+ e -> Logger.error("error: #{inspect(e)}")
+ end
+
+ :ok
+ end
+
+ def publish(%Activity{data: %{"type" => "Create"}} = activity) do
+ with %User{} = user <- get_actor(),
+ %Object{} = object <- Object.normalize(activity.data["object"]["id"]) do
+ ActivityPub.announce(user, object)
+ else
+ e -> Logger.error("error: #{inspect(e)}")
+ end
+ end
+
+ def publish(_), do: nil
+end
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index 7cdc1656b..0664b5a2e 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -306,6 +306,24 @@ defmodule Pleroma.Web.ActivityPub.Utils do
@doc """
Make announce activity data for the given actor and object
"""
+ # for relayed messages, we only want to send to subscribers
+ def make_announce_data(
+ %User{ap_id: ap_id, nickname: nil} = user,
+ %Object{data: %{"id" => id}} = object,
+ activity_id
+ ) do
+ data = %{
+ "type" => "Announce",
+ "actor" => ap_id,
+ "object" => id,
+ "to" => [user.follower_address],
+ "cc" => [],
+ "context" => object.data["context"]
+ }
+
+ if activity_id, do: Map.put(data, "id", activity_id), else: data
+ end
+
def make_announce_data(
%User{ap_id: ap_id} = user,
%Object{data: %{"id" => id}} = object,
@@ -360,7 +378,12 @@ defmodule Pleroma.Web.ActivityPub.Utils do
if activity_id, do: Map.put(data, "id", activity_id), else: data
end
- def add_announce_to_object(%Activity{data: %{"actor" => actor}}, object) do
+ def add_announce_to_object(
+ %Activity{
+ data: %{"actor" => actor, "cc" => ["https://www.w3.org/ns/activitystreams#Public"]}
+ },
+ object
+ ) do
announcements =
if is_list(object.data["announcements"]), do: object.data["announcements"], else: []
@@ -369,6 +392,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
end
end
+ def add_announce_to_object(_, object), do: {:ok, object}
+
def remove_announce_from_object(%Activity{data: %{"actor" => actor}}, object) do
announcements =
if is_list(object.data["announcements"]), do: object.data["announcements"], else: []
diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex
index fc76f2940..16419e1b7 100644
--- a/lib/pleroma/web/activity_pub/views/user_view.ex
+++ b/lib/pleroma/web/activity_pub/views/user_view.ex
@@ -9,6 +9,35 @@ defmodule Pleroma.Web.ActivityPub.UserView do
alias Pleroma.Web.ActivityPub.Utils
import Ecto.Query
+ # the instance itself is not a Person, but instead an Application
+ def render("user.json", %{user: %{nickname: nil} = user}) do
+ {:ok, user} = WebFinger.ensure_keys_present(user)
+ {:ok, _, public_key} = Salmon.keys_from_pem(user.info["keys"])
+ public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
+ public_key = :public_key.pem_encode([public_key])
+
+ %{
+ "@context" => "https://www.w3.org/ns/activitystreams",
+ "id" => user.ap_id,
+ "type" => "Application",
+ "following" => "#{user.ap_id}/following",
+ "followers" => "#{user.ap_id}/followers",
+ "inbox" => "#{user.ap_id}/inbox",
+ "name" => "Pleroma",
+ "summary" => "Virtual actor for Pleroma relay",
+ "url" => user.ap_id,
+ "manuallyApprovesFollowers" => false,
+ "publicKey" => %{
+ "id" => "#{user.ap_id}#main-key",
+ "owner" => user.ap_id,
+ "publicKeyPem" => public_key
+ },
+ "endpoints" => %{
+ "sharedInbox" => "#{Pleroma.Web.Endpoint.url()}/inbox"
+ }
+ }
+ end
+
def render("user.json", %{user: user}) do
{:ok, user} = WebFinger.ensure_keys_present(user)
{:ok, _, public_key} = Salmon.keys_from_pem(user.info["keys"])
diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex
index ccefb0bdf..078f3ec11 100644
--- a/lib/pleroma/web/federator/federator.ex
+++ b/lib/pleroma/web/federator/federator.ex
@@ -4,6 +4,7 @@ defmodule Pleroma.Web.Federator do
alias Pleroma.Activity
alias Pleroma.Web.{WebFinger, Websub}
alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Pleroma.Web.ActivityPub.Relay
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ActivityPub.Utils
require Logger
@@ -69,6 +70,11 @@ defmodule Pleroma.Web.Federator do
Logger.info(fn -> "Sending #{activity.data["id"]} out via Salmon" end)
Pleroma.Web.Salmon.publish(actor, activity)
+
+ if Mix.env() != :test do
+ Logger.info(fn -> "Relaying #{activity.data["id"]} out" end)
+ Pleroma.Web.ActivityPub.Relay.publish(activity)
+ end
end
Logger.info(fn -> "Sending #{activity.data["id"]} out via AP" end)
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 68e159f6a..927323794 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -5,6 +5,7 @@ defmodule Pleroma.Web.Router do
@instance Application.get_env(:pleroma, :instance)
@federating Keyword.get(@instance, :federating)
+ @allow_relay Keyword.get(@instance, :allow_relay)
@public Keyword.get(@instance, :public)
@registrations_open Keyword.get(@instance, :registrations_open)
@@ -293,6 +294,10 @@ defmodule Pleroma.Web.Router do
get("/externalprofile/show", TwitterAPI.Controller, :external_profile)
end
+ pipeline :ap_relay do
+ plug(:accepts, ["activity+json"])
+ end
+
pipeline :ostatus do
plug(:accepts, ["xml", "atom", "html", "activity+json"])
end
@@ -329,6 +334,13 @@ defmodule Pleroma.Web.Router do
end
if @federating do
+ if @allow_relay do
+ scope "/relay", Pleroma.Web.ActivityPub do
+ pipe_through(:ap_relay)
+ get("/", ActivityPubController, :relay)
+ end
+ end
+
scope "/", Pleroma.Web.ActivityPub do
pipe_through(:activitypub)
post("/users/:nickname/inbox", ActivityPubController, :inbox)