Docs + test for DM privacy
Some checks are pending
ci/woodpecker/pr/build-amd64 Pipeline is pending
ci/woodpecker/pr/build-arm64 Pipeline is pending
ci/woodpecker/pr/docs Pipeline is pending
ci/woodpecker/pr/lint Pipeline is pending
ci/woodpecker/pr/test Pipeline is pending

This commit is contained in:
smitten 2023-07-29 00:08:29 -04:00
parent 208d2a6e0d
commit 5469a1268e
Signed by untrusted user: smitten
GPG key ID: 1DDD22F13552A07A
2 changed files with 86 additions and 48 deletions

View file

@ -3,6 +3,11 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AkkomaAPI.ProtocolHandlerController do
@moduledoc """
Handles web-based protocol requests, in particular web+ap: which reference ActivityPub URIs.
see https://datatracker.ietf.org/doc/draft-soni-protocol-handler-well-known-uri/
A web+ap: URI should be handled like an https: URI, redirecting to the local representation of the remote or local object.
"""
use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
@ -17,7 +22,6 @@ defmodule Pleroma.Web.AkkomaAPI.ProtocolHandlerController do
# Note: (requires read:search)
plug(OAuthScopesPlug, %{scopes: ["read:search"], fallback: :proceed_unauthenticated} when action in @oauth_search_actions)
# Protocol definition: https://datatracker.ietf.org/doc/draft-soni-protocol-handler-well-known-uri/
def reroute(conn, %{"target" => target_param}) do
conn |> redirect(to: "/api/v1/akkoma/protocol-handler?#{URI.encode_query([target: target_param])}")
end
@ -38,12 +42,13 @@ def handle(%{assigns: %{user: user}} = conn, %{"target" => "web+ap:" <> identifi
def handle(conn, _), do: conn |> json_response(:bad_request, "Could not handle protocol URL")
@spec find_and_redirect(Plug.Conn.t(), String.t()) :: Plug.Conn.t()
defp find_and_redirect(%{assigns: %{user: user}} = conn, identifier) do
# Remove userinfo if present (username:password@)
cleaned = URI.parse("https:" <> identifier) |> Map.merge(%{ userinfo: nil }) |> URI.to_string()
with {:error, _err} <- User.get_or_fetch(cleaned),
[] <- DatabaseSearch.maybe_fetch([], user, cleaned),
[] <- exact_search(cleaned, user) do
[] <- exact_user_search(cleaned, user) do
conn |> json_response(:not_found, "Not Found - #{cleaned}")
else
{:ok, %User{} = found_user} -> conn |> redirect(to: "/users/#{found_user.id}")
@ -54,7 +59,7 @@ defp find_and_redirect(%{assigns: %{user: user}} = conn, identifier) do
end
end
defp exact_search(identifier, user) do
defp exact_user_search(identifier, user) do
case User.search(identifier, limit: 1, for_user: user) do
[%User{:ap_id => ^identifier} = found_user] -> [found_user]
[%User{:uri => ^identifier} = found_user] -> [found_user]

View file

@ -6,7 +6,33 @@ defmodule Pleroma.Web.AkkomaAPI.ProtocolHandlerControllerTest do
import Pleroma.Factory
describe "GET /.well-known/protocol-handler" do
test "should return bad_request when missing `target`" do
%{conn: conn} = oauth_access([])
resp =
conn
|> get("/.well-known/protocol-handler")
|> json_response(400)
assert resp =~ "Missing `target` parameter"
end
test "should return redirect when target parameter is present" do
%{conn: conn} = oauth_access([])
resp =
conn
|> get("/.well-known/protocol-handler?target=web+ap://outerheaven.club/objects/337fca0c-6282-4142-9491-df51ac917504")
|> html_response(302)
assert resp =~ "You are being"
end
end
describe "GET /api/v1/akkoma/protocol-handler" do
setup do
clear_config([Pleroma.Web.Endpoint, :url, :host], "sub.example.com")
Tesla.Mock.mock(fn
%{method: :get, url: "https://mastodon.social/users/emelie/statuses/101849165031453009"} ->
%Tesla.Env{
@ -35,34 +61,12 @@ defmodule Pleroma.Web.AkkomaAPI.ProtocolHandlerControllerTest do
|> String.replace("{{domain}}", "mastodon.social")
|> String.replace("{{nickname}}", "emelie")
}
_ -> %Tesla.Env{
status: 404,
}
end)
end
describe "GET /.well-known/protocol-handler" do
test "should return bad_request when missing `target`" do
%{conn: conn} = oauth_access([])
resp =
conn
|> get("/.well-known/protocol-handler")
|> json_response(400)
assert resp =~ "Missing `target` parameter"
end
test "should return redirect when target parameter is present" do
%{conn: conn} = oauth_access([])
resp =
conn
|> get("/.well-known/protocol-handler?target=web+ap://outerheaven.club/objects/337fca0c-6282-4142-9491-df51ac917504")
|> html_response(302)
assert resp =~ "You are being"
end
end
describe "GET /api/v1/akkoma/protocol-handler" do
test "should return bad_request when target prefix has unknown protocol" do
%{conn: conn} = oauth_access([])
@ -75,7 +79,6 @@ test "should return bad_request when target prefix has unknown protocol" do
end
test "should return forbidden for unauthed user when target is remote" do
clear_config([Pleroma.Web.Endpoint, :url, :host], "sub.example.com")
%{conn: conn} = oauth_access([])
resp =
@ -87,7 +90,6 @@ test "should return forbidden for unauthed user when target is remote" do
end
test "should return redirect for unauthed user when target is local AP ID for user" do
clear_config([Pleroma.Web.Endpoint, :url, :host], "sub.example.com")
%{conn: conn} = oauth_access([])
local_user = insert(:user, %{nickname: "akkoma@sub.example.com", local: true, ap_id: "https://sub.example.com/users/akkoma"})
@ -100,23 +102,54 @@ test "should return redirect for unauthed user when target is local AP ID for us
assert resp =~ "<a href=\"/users/#{local_user.id}\">"
end
test "should return redirect for unauthed user when target is local AP ID for note activity" do
clear_config([Pleroma.Web.Endpoint, :url, :host], "mastodon.social")
test "should return not_found for unauthed user when target is local AP ID for DM note activity" do
%{conn: conn} = oauth_access([])
local_user = insert(:user, %{nickname: "akkoma@sub.example.com", local: true, ap_id: "https://sub.example.com/users/akkoma"})
note = insert(:note, %{
id: "AYAsX3ZRH6NJAzZmEa",
data: %{
"cc" => [],
"to" => [],
"actor" => local_user.ap_id,
"id" => "https://sub.example.com/notice/AYAsX3ZRH6NJAzZmEa",
"summary" => "",
"content" => "Pleroma's really cool!",
"directMessage" => true,
}
})
insert(:note_activity, note: note, user: local_user)
clear_config([Pleroma.Web.Endpoint, :url, :host], "sub.example.com")
%{conn: conn} = oauth_access(["read:search"])
conn
|> get("/api/v1/akkoma/protocol-handler?target=web%2Bap%3A%2F%2Fsub.example.com/notice/AYAsX3ZRH6NJAzZmEa")
|> json_response(404)
end
test "should return not_found for unauthed user when target is local AP ID for public note activity" do
%{conn: conn} = oauth_access([])
local_user = insert(:user, %{nickname: "akkoma@sub.example.com", local: true, ap_id: "https://sub.example.com/users/akkoma"})
note = insert(:note, %{
id: "AYAsX3ZRH6NJAzZmPa",
data: %{
"cc" => [],
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"actor" => local_user.ap_id,
"id" => "https://sub.example.com/notice/AYAsX3ZRH6NJAzZmPa",
"summary" => "",
"content" => "Pleroma's really cool!",
}
})
activity = insert(:note_activity, note: note, user: local_user, visibility: "direct")
resp =
conn
|> get("/api/v1/akkoma/protocol-handler?target=web%2Bap%3A%2F%2Fmastodon.social/users/emelie/statuses/101849165031453009")
|> get("/api/v1/akkoma/protocol-handler?target=web%2Bap%3A%2F%2Fsub.example.com/notice/AYAsX3ZRH6NJAzZmPa")
|> html_response(302)
assert activity = Activity.get_by_object_ap_id_with_object("https://mastodon.social/users/emelie/statuses/101849165031453009")
assert resp =~ "You are being"
assert resp =~ "<a href=\"/notice/#{activity.id}\">"
end
test "should return redirect for authed user when target is AP ID for user" do
test "should return redirect for authed user when target is AP ID for remote user" do
%{conn: conn} = oauth_access(["read:search"])
remote_user = insert(:user, %{nickname: "akkoma@ihatebeinga.live", local: false, ap_id: "https://ihatebeinga.live/users/akkoma"})
@ -129,7 +162,7 @@ test "should return redirect for authed user when target is AP ID for user" do
assert resp =~ "<a href=\"/users/#{remote_user.id}\">"
end
test "should return redirect for authed user when target is URL for user" do
test "should return redirect for authed user when target is URI for remote user" do
%{conn: conn} = oauth_access(["read:search"])
remote_user = insert(:user, %{
nickname: "emelie@mastodon.social",
@ -160,7 +193,7 @@ test "should return redirect for authed user when target is AP ID for user, stri
assert resp =~ "<a href=\"/users/#{remote_user.id}\">"
end
test "should return redirect for authed user when target is AP ID for note activity" do
test "should return redirect for authed user when target is AP ID for remote note activity" do
%{conn: conn} = oauth_access(["read:search"])
resp =