Merge branch 'activitypub-c2s' into 'develop'
Activitypub c2s See merge request pleroma/pleroma!608
This commit is contained in:
commit
dfde2622d0
|
@ -4,11 +4,12 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
alias Pleroma.{User, Object}
|
alias Pleroma.{Activity, User, Object}
|
||||||
alias Pleroma.Web.ActivityPub.{ObjectView, UserView}
|
alias Pleroma.Web.ActivityPub.{ObjectView, UserView}
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
alias Pleroma.Web.Federator
|
alias Pleroma.Web.Federator
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
@ -93,19 +94,15 @@ def followers(conn, %{"nickname" => nickname}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def outbox(conn, %{"nickname" => nickname, "max_id" => max_id}) do
|
def outbox(conn, %{"nickname" => nickname} = params) do
|
||||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||||
{:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
|
{:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
|
||||||
conn
|
conn
|
||||||
|> put_resp_header("content-type", "application/activity+json")
|
|> put_resp_header("content-type", "application/activity+json")
|
||||||
|> json(UserView.render("outbox.json", %{user: user, max_id: max_id}))
|
|> json(UserView.render("outbox.json", %{user: user, max_id: params["max_id"]}))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def outbox(conn, %{"nickname" => nickname}) do
|
|
||||||
outbox(conn, %{"nickname" => nickname, "max_id" => nil})
|
|
||||||
end
|
|
||||||
|
|
||||||
def inbox(%{assigns: %{valid_signature: true}} = conn, %{"nickname" => nickname} = params) do
|
def inbox(%{assigns: %{valid_signature: true}} = conn, %{"nickname" => nickname} = params) do
|
||||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||||
true <- Utils.recipient_in_message(user.ap_id, params),
|
true <- Utils.recipient_in_message(user.ap_id, params),
|
||||||
|
@ -156,6 +153,57 @@ def relay(conn, _params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def read_inbox(%{assigns: %{user: user}} = conn, %{"nickname" => nickname} = params) do
|
||||||
|
if nickname == user.nickname do
|
||||||
|
conn
|
||||||
|
|> put_resp_header("content-type", "application/activity+json")
|
||||||
|
|> json(UserView.render("inbox.json", %{user: user, max_id: params["max_id"]}))
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
|> put_status(:forbidden)
|
||||||
|
|> json("can't read inbox of #{nickname} as #{user.nickname}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_outbox(
|
||||||
|
%{assigns: %{user: user}} = conn,
|
||||||
|
%{"nickname" => nickname, "type" => "Create"} = params
|
||||||
|
) do
|
||||||
|
if nickname == user.nickname do
|
||||||
|
actor = user.ap_id()
|
||||||
|
|
||||||
|
params =
|
||||||
|
params
|
||||||
|
|> Map.drop(["id"])
|
||||||
|
|> Map.put("actor", actor)
|
||||||
|
|> Transmogrifier.fix_addressing()
|
||||||
|
|
||||||
|
object =
|
||||||
|
params["object"]
|
||||||
|
|> Map.merge(Map.take(params, ["to", "cc"]))
|
||||||
|
|> Map.put("attributedTo", actor)
|
||||||
|
|> Transmogrifier.fix_object()
|
||||||
|
|
||||||
|
with {:ok, %Activity{} = activity} <-
|
||||||
|
ActivityPub.create(%{
|
||||||
|
to: params["to"],
|
||||||
|
actor: user,
|
||||||
|
context: object["context"],
|
||||||
|
object: object,
|
||||||
|
additional: Map.take(params, ["cc"])
|
||||||
|
}) do
|
||||||
|
conn
|
||||||
|
|> put_status(:created)
|
||||||
|
|> put_resp_header("location", activity.data["id"])
|
||||||
|
|> json(activity.data)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
|> put_status(:forbidden)
|
||||||
|
|> json("can't update outbox of #{nickname} as #{user.nickname}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def errors(conn, {:error, :not_found}) do
|
def errors(conn, {:error, :not_found}) do
|
||||||
conn
|
conn
|
||||||
|> put_status(404)
|
|> put_status(404)
|
||||||
|
|
|
@ -176,6 +176,53 @@ def render("outbox.json", %{user: user, max_id: max_qid}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def render("inbox.json", %{user: user, max_id: max_qid}) do
|
||||||
|
params = %{
|
||||||
|
"limit" => "10"
|
||||||
|
}
|
||||||
|
|
||||||
|
params =
|
||||||
|
if max_qid != nil do
|
||||||
|
Map.put(params, "max_id", max_qid)
|
||||||
|
else
|
||||||
|
params
|
||||||
|
end
|
||||||
|
|
||||||
|
activities = ActivityPub.fetch_activities([user.ap_id | user.following], params)
|
||||||
|
|
||||||
|
min_id = Enum.at(Enum.reverse(activities), 0).id
|
||||||
|
max_id = Enum.at(activities, 0).id
|
||||||
|
|
||||||
|
collection =
|
||||||
|
Enum.map(activities, fn act ->
|
||||||
|
{:ok, data} = Transmogrifier.prepare_outgoing(act.data)
|
||||||
|
data
|
||||||
|
end)
|
||||||
|
|
||||||
|
iri = "#{user.ap_id}/inbox"
|
||||||
|
|
||||||
|
page = %{
|
||||||
|
"id" => "#{iri}?max_id=#{max_id}",
|
||||||
|
"type" => "OrderedCollectionPage",
|
||||||
|
"partOf" => iri,
|
||||||
|
"totalItems" => -1,
|
||||||
|
"orderedItems" => collection,
|
||||||
|
"next" => "#{iri}?max_id=#{min_id - 1}"
|
||||||
|
}
|
||||||
|
|
||||||
|
if max_qid == nil do
|
||||||
|
%{
|
||||||
|
"id" => iri,
|
||||||
|
"type" => "OrderedCollection",
|
||||||
|
"totalItems" => -1,
|
||||||
|
"first" => page
|
||||||
|
}
|
||||||
|
|> Map.merge(Utils.make_json_ld_header())
|
||||||
|
else
|
||||||
|
page |> Map.merge(Utils.make_json_ld_header())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def collection(collection, iri, page, show_items \\ true, total \\ nil) do
|
def collection(collection, iri, page, show_items \\ true, total \\ nil) do
|
||||||
offset = (page - 1) * 10
|
offset = (page - 1) * 10
|
||||||
items = Enum.slice(collection, offset, 10)
|
items = Enum.slice(collection, offset, 10)
|
||||||
|
|
|
@ -412,6 +412,27 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/users/:nickname/outbox", ActivityPubController, :outbox)
|
get("/users/:nickname/outbox", ActivityPubController, :outbox)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
pipeline :activitypub_client do
|
||||||
|
plug(:accepts, ["activity+json"])
|
||||||
|
plug(:fetch_session)
|
||||||
|
plug(Pleroma.Plugs.OAuthPlug)
|
||||||
|
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
||||||
|
plug(Pleroma.Plugs.UserFetcherPlug)
|
||||||
|
plug(Pleroma.Plugs.SessionAuthenticationPlug)
|
||||||
|
plug(Pleroma.Plugs.LegacyAuthenticationPlug)
|
||||||
|
plug(Pleroma.Plugs.AuthenticationPlug)
|
||||||
|
plug(Pleroma.Plugs.UserEnabledPlug)
|
||||||
|
plug(Pleroma.Plugs.SetUserSessionIdPlug)
|
||||||
|
plug(Pleroma.Plugs.EnsureUserKeyPlug)
|
||||||
|
end
|
||||||
|
|
||||||
|
scope "/", Pleroma.Web.ActivityPub do
|
||||||
|
pipe_through([:activitypub_client])
|
||||||
|
|
||||||
|
get("/users/:nickname/inbox", ActivityPubController, :read_inbox)
|
||||||
|
post("/users/:nickname/outbox", ActivityPubController, :update_outbox)
|
||||||
|
end
|
||||||
|
|
||||||
scope "/relay", Pleroma.Web.ActivityPub do
|
scope "/relay", Pleroma.Web.ActivityPub do
|
||||||
pipe_through(:ap_relay)
|
pipe_through(:ap_relay)
|
||||||
get("/", ActivityPubController, :relay)
|
get("/", ActivityPubController, :relay)
|
||||||
|
|
9
test/fixtures/activitypub-client-post-activity.json
vendored
Normal file
9
test/fixtures/activitypub-client-post-activity.json
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"@context": ["https://www.w3.org/ns/activitystreams", {"@language": "en-GB"}],
|
||||||
|
"type": "Create",
|
||||||
|
"object": {
|
||||||
|
"type": "Note",
|
||||||
|
"content": "It's a note"
|
||||||
|
},
|
||||||
|
"to": ["https://www.w3.org/ns/activitystreams#Public"]
|
||||||
|
}
|
|
@ -112,6 +112,32 @@ test "it inserts an incoming activity into the database", %{conn: conn} do
|
||||||
:timer.sleep(500)
|
:timer.sleep(500)
|
||||||
assert Activity.get_by_ap_id(data["id"])
|
assert Activity.get_by_ap_id(data["id"])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it rejects reads from other users", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
otheruser = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, otheruser)
|
||||||
|
|> put_req_header("accept", "application/activity+json")
|
||||||
|
|> get("/users/#{user.nickname}/inbox")
|
||||||
|
|
||||||
|
assert json_response(conn, 403)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns a note activity in a collection", %{conn: conn} do
|
||||||
|
note_activity = insert(:direct_note_activity)
|
||||||
|
user = User.get_cached_by_ap_id(hd(note_activity.data["to"]))
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> put_req_header("accept", "application/activity+json")
|
||||||
|
|> get("/users/#{user.nickname}/inbox")
|
||||||
|
|
||||||
|
assert response(conn, 200) =~ note_activity.data["object"]["content"]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "/users/:nickname/outbox" do
|
describe "/users/:nickname/outbox" do
|
||||||
|
@ -138,6 +164,34 @@ test "it returns an announce activity in a collection", %{conn: conn} do
|
||||||
|
|
||||||
assert response(conn, 200) =~ announce_activity.data["object"]
|
assert response(conn, 200) =~ announce_activity.data["object"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it rejects posts from other users", %{conn: conn} do
|
||||||
|
data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
|
||||||
|
user = insert(:user)
|
||||||
|
otheruser = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, otheruser)
|
||||||
|
|> put_req_header("content-type", "application/activity+json")
|
||||||
|
|> post("/users/#{user.nickname}/outbox", data)
|
||||||
|
|
||||||
|
assert json_response(conn, 403)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it inserts an incoming activity into the database", %{conn: conn} do
|
||||||
|
data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> put_req_header("content-type", "application/activity+json")
|
||||||
|
|> post("/users/#{user.nickname}/outbox", data)
|
||||||
|
|
||||||
|
result = json_response(conn, 201)
|
||||||
|
assert Activity.get_by_ap_id(result["id"])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "/users/:nickname/followers" do
|
describe "/users/:nickname/followers" do
|
||||||
|
|
Loading…
Reference in a new issue