forked from AkkomaGang/akkoma
Merge pull request 'Remove unused AP C2S endpoints' (#749) from who-wants-to-yeet-c2s-i-want-to-yeet-c2s into develop
Reviewed-on: AkkomaGang/akkoma#749
This commit is contained in:
commit
1e48a37545
4 changed files with 8 additions and 462 deletions
|
@ -12,9 +12,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.InternalFetchActor
|
||||
alias Pleroma.Web.ActivityPub.ObjectView
|
||||
alias Pleroma.Web.ActivityPub.Pipeline
|
||||
alias Pleroma.Web.ActivityPub.Relay
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.ActivityPub.UserView
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
|
@ -40,11 +38,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
# Note: :following and :followers must be served even without authentication (as via :api)
|
||||
plug(
|
||||
EnsureAuthenticatedPlug
|
||||
when action in [:read_inbox, :update_outbox, :whoami, :upload_media]
|
||||
when action in [:read_inbox]
|
||||
)
|
||||
|
||||
plug(Majic.Plug, [pool: Pleroma.MajicPool] when action in [:upload_media])
|
||||
|
||||
plug(
|
||||
Pleroma.Web.Plugs.Cache,
|
||||
[query_params: false, tracking_fun: &__MODULE__.track_object_fetch/2]
|
||||
|
@ -160,7 +156,9 @@ def maybe_skip_cache(conn, user) do
|
|||
end
|
||||
end
|
||||
|
||||
# GET /relay/following
|
||||
@doc """
|
||||
GET /relay/following
|
||||
"""
|
||||
def relay_following(conn, _params) do
|
||||
with %{halted: false} = conn <- FederatingPlug.call(conn, []) do
|
||||
conn
|
||||
|
@ -197,7 +195,9 @@ def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname}) d
|
|||
end
|
||||
end
|
||||
|
||||
# GET /relay/followers
|
||||
@doc """
|
||||
GET /relay/followers
|
||||
"""
|
||||
def relay_followers(conn, _params) do
|
||||
with %{halted: false} = conn <- FederatingPlug.call(conn, []) do
|
||||
conn
|
||||
|
@ -317,14 +317,6 @@ def internal_fetch(conn, _params) do
|
|||
|> represent_service_actor(conn)
|
||||
end
|
||||
|
||||
@doc "Returns the authenticated user's ActivityPub User object or a 404 Not Found if non-authenticated"
|
||||
def whoami(%{assigns: %{user: %User{} = user}} = conn, _params) do
|
||||
conn
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> put_view(UserView)
|
||||
|> render("user.json", %{user: user})
|
||||
end
|
||||
|
||||
def read_inbox(
|
||||
%{assigns: %{user: %User{nickname: nickname} = user}} = conn,
|
||||
%{"nickname" => nickname, "page" => page?} = params
|
||||
|
@ -375,105 +367,6 @@ def read_inbox(%{assigns: %{user: %User{nickname: as_nickname}}} = conn, %{
|
|||
|> json(err)
|
||||
end
|
||||
|
||||
defp fix_user_message(%User{ap_id: actor}, %{"type" => "Create", "object" => object} = activity)
|
||||
when is_map(object) do
|
||||
length =
|
||||
[object["content"], object["summary"], object["name"]]
|
||||
|> Enum.filter(&is_binary(&1))
|
||||
|> Enum.join("")
|
||||
|> String.length()
|
||||
|
||||
limit = Pleroma.Config.get([:instance, :limit])
|
||||
|
||||
if length < limit do
|
||||
object =
|
||||
object
|
||||
|> Transmogrifier.strip_internal_fields()
|
||||
|> Map.put("attributedTo", actor)
|
||||
|> Map.put("actor", actor)
|
||||
|> Map.put("id", Utils.generate_object_id())
|
||||
|
||||
{:ok, Map.put(activity, "object", object)}
|
||||
else
|
||||
{:error,
|
||||
dgettext(
|
||||
"errors",
|
||||
"Character limit (%{limit} characters) exceeded, contains %{length} characters",
|
||||
limit: limit,
|
||||
length: length
|
||||
)}
|
||||
end
|
||||
end
|
||||
|
||||
defp fix_user_message(
|
||||
%User{ap_id: actor} = user,
|
||||
%{"type" => "Delete", "object" => object} = activity
|
||||
) do
|
||||
with {_, %Object{data: object_data}} <- {:normalize, Object.normalize(object, fetch: false)},
|
||||
{_, true} <- {:permission, user.is_moderator || actor == object_data["actor"]} do
|
||||
{:ok, activity}
|
||||
else
|
||||
{:normalize, _} ->
|
||||
{:error, "No such object found"}
|
||||
|
||||
{:permission, _} ->
|
||||
{:forbidden, "You can't delete this object"}
|
||||
end
|
||||
end
|
||||
|
||||
defp fix_user_message(%User{}, activity) do
|
||||
{:ok, activity}
|
||||
end
|
||||
|
||||
def update_outbox(
|
||||
%{assigns: %{user: %User{nickname: nickname, ap_id: actor} = user}} = conn,
|
||||
%{"nickname" => nickname} = params
|
||||
) do
|
||||
params =
|
||||
params
|
||||
|> Map.drop(["nickname"])
|
||||
|> Map.put("id", Utils.generate_activity_id())
|
||||
|> Map.put("actor", actor)
|
||||
|
||||
with {:ok, params} <- fix_user_message(user, params),
|
||||
{:ok, activity, _} <- Pipeline.common_pipeline(params, local: true),
|
||||
%Activity{data: activity_data} <- Activity.normalize(activity) do
|
||||
conn
|
||||
|> put_status(:created)
|
||||
|> put_resp_header("location", activity_data["id"])
|
||||
|> json(activity_data)
|
||||
else
|
||||
{:forbidden, message} ->
|
||||
conn
|
||||
|> put_status(:forbidden)
|
||||
|> json(message)
|
||||
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(message)
|
||||
|
||||
e ->
|
||||
Logger.warning(fn -> "AP C2S: #{inspect(e)}" end)
|
||||
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json("Bad Request")
|
||||
end
|
||||
end
|
||||
|
||||
def update_outbox(%{assigns: %{user: %User{} = user}} = conn, %{"nickname" => nickname}) do
|
||||
err =
|
||||
dgettext("errors", "can't update outbox of %{nickname} as %{as_nickname}",
|
||||
nickname: nickname,
|
||||
as_nickname: user.nickname
|
||||
)
|
||||
|
||||
conn
|
||||
|> put_status(:forbidden)
|
||||
|> json(err)
|
||||
end
|
||||
|
||||
defp errors(conn, {:error, :not_found}) do
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|
@ -495,21 +388,6 @@ defp set_requester_reachable(%Plug.Conn{} = conn, _) do
|
|||
conn
|
||||
end
|
||||
|
||||
def upload_media(%{assigns: %{user: %User{} = user}} = conn, %{"file" => file} = data) do
|
||||
with {:ok, object} <-
|
||||
ActivityPub.upload(
|
||||
file,
|
||||
actor: User.ap_id(user),
|
||||
description: Map.get(data, "description")
|
||||
) do
|
||||
Logger.debug(inspect(object))
|
||||
|
||||
conn
|
||||
|> put_status(:created)
|
||||
|> json(object.data)
|
||||
end
|
||||
end
|
||||
|
||||
def pinned(conn, %{"nickname" => nickname}) do
|
||||
with %User{} = user <- User.get_cached_by_nickname(nickname) do
|
||||
conn
|
||||
|
|
|
@ -26,8 +26,7 @@ def render("endpoints.json", %{user: %User{local: true} = _user}) do
|
|||
"oauthAuthorizationEndpoint" => url(~p"/oauth/authorize"),
|
||||
"oauthRegistrationEndpoint" => url(~p"/api/v1/apps"),
|
||||
"oauthTokenEndpoint" => url(~p"/oauth/token"),
|
||||
"sharedInbox" => url(~p"/inbox"),
|
||||
"uploadMedia" => url(~p"/api/ap/upload_media")
|
||||
"sharedInbox" => url(~p"/inbox")
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -800,13 +800,9 @@ defmodule Pleroma.Web.Router do
|
|||
scope "/", Pleroma.Web.ActivityPub do
|
||||
pipe_through([:activitypub_client])
|
||||
|
||||
get("/api/ap/whoami", ActivityPubController, :whoami)
|
||||
get("/users/:nickname/inbox", ActivityPubController, :read_inbox)
|
||||
|
||||
get("/users/:nickname/outbox", ActivityPubController, :outbox)
|
||||
post("/users/:nickname/outbox", ActivityPubController, :update_outbox)
|
||||
post("/api/ap/upload_media", ActivityPubController, :upload_media)
|
||||
|
||||
get("/users/:nickname/collections/featured", ActivityPubController, :pinned)
|
||||
end
|
||||
|
||||
|
|
|
@ -1418,244 +1418,6 @@ test "It returns poll Answers when authenticated", %{conn: conn} do
|
|||
end
|
||||
end
|
||||
|
||||
describe "POST /users/:nickname/outbox (C2S)" do
|
||||
setup do: clear_config([:instance, :limit])
|
||||
|
||||
setup do
|
||||
[
|
||||
activity: %{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Create",
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"content" => "AP C2S test",
|
||||
"to" => "https://www.w3.org/ns/activitystreams#Public",
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
test "it rejects posts from other users / unauthenticated users", %{
|
||||
conn: conn,
|
||||
activity: activity
|
||||
} do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
conn = put_req_header(conn, "content-type", "application/activity+json")
|
||||
|
||||
conn
|
||||
|> post("/users/#{user.nickname}/outbox", activity)
|
||||
|> json_response(403)
|
||||
|
||||
conn
|
||||
|> assign(:user, other_user)
|
||||
|> post("/users/#{user.nickname}/outbox", activity)
|
||||
|> json_response(403)
|
||||
end
|
||||
|
||||
test "it inserts an incoming create activity into the database", %{
|
||||
conn: conn,
|
||||
activity: activity
|
||||
} do
|
||||
user = insert(:user)
|
||||
|
||||
result =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/outbox", activity)
|
||||
|> json_response(201)
|
||||
|
||||
assert Activity.get_by_ap_id(result["id"])
|
||||
assert result["object"]
|
||||
assert %Object{data: object} = Object.normalize(result["object"], fetch: false)
|
||||
assert object["content"] == activity["object"]["content"]
|
||||
end
|
||||
|
||||
test "it rejects anything beyond 'Note' creations", %{conn: conn, activity: activity} do
|
||||
user = insert(:user)
|
||||
|
||||
activity =
|
||||
activity
|
||||
|> put_in(["object", "type"], "Benis")
|
||||
|
||||
_result =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/outbox", activity)
|
||||
|> json_response(400)
|
||||
end
|
||||
|
||||
test "it inserts an incoming sensitive activity into the database", %{
|
||||
conn: conn,
|
||||
activity: activity
|
||||
} do
|
||||
user = insert(:user)
|
||||
conn = assign(conn, :user, user)
|
||||
object = Map.put(activity["object"], "sensitive", true)
|
||||
activity = Map.put(activity, "object", object)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/outbox", activity)
|
||||
|> json_response(201)
|
||||
|
||||
assert Activity.get_by_ap_id(response["id"])
|
||||
assert response["object"]
|
||||
assert %Object{data: response_object} = Object.normalize(response["object"], fetch: false)
|
||||
assert response_object["sensitive"] == true
|
||||
assert response_object["content"] == activity["object"]["content"]
|
||||
|
||||
representation =
|
||||
conn
|
||||
|> put_req_header("accept", "application/activity+json")
|
||||
|> get(response["id"])
|
||||
|> json_response(200)
|
||||
|
||||
assert representation["object"]["sensitive"] == true
|
||||
end
|
||||
|
||||
test "it rejects an incoming activity with bogus type", %{conn: conn, activity: activity} do
|
||||
user = insert(:user)
|
||||
activity = Map.put(activity, "type", "BadType")
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/outbox", activity)
|
||||
|
||||
assert json_response(conn, 400)
|
||||
end
|
||||
|
||||
test "it erects a tombstone when receiving a delete activity", %{conn: conn} do
|
||||
note_activity = insert(:note_activity)
|
||||
note_object = Object.normalize(note_activity, fetch: false)
|
||||
user = User.get_cached_by_ap_id(note_activity.data["actor"])
|
||||
|
||||
data = %{
|
||||
"type" => "Delete",
|
||||
"object" => %{
|
||||
"id" => note_object.data["id"]
|
||||
}
|
||||
}
|
||||
|
||||
result =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/outbox", data)
|
||||
|> json_response(201)
|
||||
|
||||
assert Activity.get_by_ap_id(result["id"])
|
||||
|
||||
assert object = Object.get_by_ap_id(note_object.data["id"])
|
||||
assert object.data["type"] == "Tombstone"
|
||||
end
|
||||
|
||||
test "it rejects delete activity of object from other actor", %{conn: conn} do
|
||||
note_activity = insert(:note_activity)
|
||||
note_object = Object.normalize(note_activity, fetch: false)
|
||||
user = insert(:user)
|
||||
|
||||
data = %{
|
||||
type: "Delete",
|
||||
object: %{
|
||||
id: note_object.data["id"]
|
||||
}
|
||||
}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/outbox", data)
|
||||
|
||||
assert json_response(conn, 403)
|
||||
end
|
||||
|
||||
test "it increases like count when receiving a like action", %{conn: conn} do
|
||||
note_activity = insert(:note_activity)
|
||||
note_object = Object.normalize(note_activity, fetch: false)
|
||||
user = User.get_cached_by_ap_id(note_activity.data["actor"])
|
||||
|
||||
data = %{
|
||||
type: "Like",
|
||||
object: %{
|
||||
id: note_object.data["id"]
|
||||
}
|
||||
}
|
||||
|
||||
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"])
|
||||
|
||||
assert object = Object.get_by_ap_id(note_object.data["id"])
|
||||
assert object.data["like_count"] == 1
|
||||
end
|
||||
|
||||
test "it doesn't spreads faulty attributedTo or actor fields", %{
|
||||
conn: conn,
|
||||
activity: activity
|
||||
} do
|
||||
reimu = insert(:user, nickname: "reimu")
|
||||
cirno = insert(:user, nickname: "cirno")
|
||||
|
||||
assert reimu.ap_id
|
||||
assert cirno.ap_id
|
||||
|
||||
activity =
|
||||
activity
|
||||
|> put_in(["object", "actor"], reimu.ap_id)
|
||||
|> put_in(["object", "attributedTo"], reimu.ap_id)
|
||||
|> put_in(["actor"], reimu.ap_id)
|
||||
|> put_in(["attributedTo"], reimu.ap_id)
|
||||
|
||||
_reimu_outbox =
|
||||
conn
|
||||
|> assign(:user, cirno)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{reimu.nickname}/outbox", activity)
|
||||
|> json_response(403)
|
||||
|
||||
cirno_outbox =
|
||||
conn
|
||||
|> assign(:user, cirno)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{cirno.nickname}/outbox", activity)
|
||||
|> json_response(201)
|
||||
|
||||
assert cirno_outbox["attributedTo"] == nil
|
||||
assert cirno_outbox["actor"] == cirno.ap_id
|
||||
|
||||
assert cirno_object = Object.normalize(cirno_outbox["object"], fetch: false)
|
||||
assert cirno_object.data["actor"] == cirno.ap_id
|
||||
assert cirno_object.data["attributedTo"] == cirno.ap_id
|
||||
end
|
||||
|
||||
test "Character limitation", %{conn: conn, activity: activity} do
|
||||
clear_config([:instance, :limit], 5)
|
||||
user = insert(:user)
|
||||
|
||||
result =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/outbox", activity)
|
||||
|> json_response(400)
|
||||
|
||||
assert result == "Character limit (5 characters) exceeded, contains 11 characters"
|
||||
end
|
||||
end
|
||||
|
||||
describe "/relay/followers" do
|
||||
test "it returns relay followers", %{conn: conn} do
|
||||
relay_actor = Relay.get_actor()
|
||||
|
@ -1977,95 +1739,6 @@ test "it tracks a signed activity fetch when the json is cached", %{conn: conn}
|
|||
end
|
||||
end
|
||||
|
||||
describe "Additional ActivityPub C2S endpoints" do
|
||||
test "GET /api/ap/whoami", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> get("/api/ap/whoami")
|
||||
|
||||
user = User.get_cached_by_id(user.id)
|
||||
|
||||
assert UserView.render("user.json", %{user: user}) == json_response(conn, 200)
|
||||
|
||||
conn
|
||||
|> get("/api/ap/whoami")
|
||||
|> json_response(403)
|
||||
end
|
||||
|
||||
setup do: clear_config([:media_proxy])
|
||||
setup do: clear_config([Pleroma.Upload])
|
||||
|
||||
test "POST /api/ap/upload_media", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
desc = "Description of the image"
|
||||
|
||||
image = %Plug.Upload{
|
||||
content_type: "image/jpeg",
|
||||
path: Path.absname("test/fixtures/image.jpg"),
|
||||
filename: "an_image.jpg"
|
||||
}
|
||||
|
||||
object =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
|
||||
|> json_response(:created)
|
||||
|
||||
assert object["name"] == desc
|
||||
assert object["type"] == "Document"
|
||||
assert object["actor"] == user.ap_id
|
||||
assert [%{"href" => object_href, "mediaType" => object_mediatype}] = object["url"]
|
||||
assert is_binary(object_href)
|
||||
assert object_mediatype == "image/jpeg"
|
||||
assert String.ends_with?(object_href, ".jpg")
|
||||
|
||||
activity_request = %{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Create",
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"content" => "AP C2S test, attachment",
|
||||
"attachment" => [object],
|
||||
"to" => "https://www.w3.org/ns/activitystreams#Public",
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
activity_response =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> post("/users/#{user.nickname}/outbox", activity_request)
|
||||
|> json_response(:created)
|
||||
|
||||
assert activity_response["id"]
|
||||
assert activity_response["object"]
|
||||
assert activity_response["actor"] == user.ap_id
|
||||
|
||||
assert %Object{data: %{"attachment" => [attachment]}} =
|
||||
Object.normalize(activity_response["object"], fetch: false)
|
||||
|
||||
assert attachment["type"] == "Document"
|
||||
assert attachment["name"] == desc
|
||||
|
||||
assert [
|
||||
%{
|
||||
"href" => ^object_href,
|
||||
"type" => "Link",
|
||||
"mediaType" => ^object_mediatype
|
||||
}
|
||||
] = attachment["url"]
|
||||
|
||||
# Fails if unauthenticated
|
||||
conn
|
||||
|> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
|
||||
|> json_response(403)
|
||||
end
|
||||
end
|
||||
|
||||
test "pinned collection", %{conn: conn} do
|
||||
clear_config([:instance, :max_pinned_statuses], 2)
|
||||
user = insert(:user)
|
||||
|
|
Loading…
Reference in a new issue