From 208d2a6e0dbacb4e020bbc81380cf36f976d5239 Mon Sep 17 00:00:00 2001 From: smitten Date: Fri, 28 Jul 2023 15:37:13 -0400 Subject: [PATCH] fallback to User.search to support profile URI Use exact search to only match on ap_id or uri --- .../protocol_handler_controller.ex | 15 ++- .../protocol_handler_controller_test.exs | 97 ++++++++++--------- 2 files changed, 64 insertions(+), 48 deletions(-) diff --git a/lib/pleroma/web/akkoma_api/controllers/protocol_handler_controller.ex b/lib/pleroma/web/akkoma_api/controllers/protocol_handler_controller.ex index ee0cdf69b..a0a521f5b 100644 --- a/lib/pleroma/web/akkoma_api/controllers/protocol_handler_controller.ex +++ b/lib/pleroma/web/akkoma_api/controllers/protocol_handler_controller.ex @@ -41,13 +41,24 @@ def handle(conn, _), do: conn |> json_response(:bad_request, "Could not handle p 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) do + with {:error, _err} <- User.get_or_fetch(cleaned), + [] <- DatabaseSearch.maybe_fetch([], user, cleaned), + [] <- exact_search(cleaned, user) do conn |> json_response(:not_found, "Not Found - #{cleaned}") else {:ok, %User{} = found_user} -> conn |> redirect(to: "/users/#{found_user.id}") + [%User{} = found_user] -> conn |> redirect(to: "/users/#{found_user.id}") + [%Activity{} = found_activity] -> conn |> redirect(to: "/notice/#{found_activity.id}") end end + + defp exact_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] + _ -> [] + end + end end diff --git a/test/pleroma/web/akkoma_api/protocol_handler_controller_test.exs b/test/pleroma/web/akkoma_api/protocol_handler_controller_test.exs index 556b94b74..5857dece8 100644 --- a/test/pleroma/web/akkoma_api/protocol_handler_controller_test.exs +++ b/test/pleroma/web/akkoma_api/protocol_handler_controller_test.exs @@ -6,6 +6,38 @@ defmodule Pleroma.Web.AkkomaAPI.ProtocolHandlerControllerTest do import Pleroma.Factory + setup do + Tesla.Mock.mock(fn + %{method: :get, url: "https://mastodon.social/users/emelie/statuses/101849165031453009"} -> + %Tesla.Env{ + status: 200, + headers: [{"content-type", "application/activity+json"}], + body: File.read!("test/fixtures/tesla_mock/status.emelie.json") + } + %{method: :get, url: "https://mastodon.social/users/emelie"} -> + %Tesla.Env{ + status: 200, + headers: [{"content-type", "application/activity+json"}], + body: File.read!("test/fixtures/tesla_mock/emelie.json") + } + %{method: :get, url: "https://mastodon.social/@emelie"} -> + %Tesla.Env{ + status: 200, + headers: [{"content-type", "application/activity+json"}], + body: File.read!("test/fixtures/tesla_mock/emelie.json") + } + %{method: :get, url: "https://mastodon.social/users/emelie/collections/featured"} -> + %Tesla.Env{ + status: 200, + headers: [{"content-type", "application/activity+json"}], + body: + File.read!("test/fixtures/users_mock/masto_featured.json") + |> String.replace("{{domain}}", "mastodon.social") + |> String.replace("{{nickname}}", "emelie") + } + end) + end + describe "GET /.well-known/protocol-handler" do test "should return bad_request when missing `target`" do %{conn: conn} = oauth_access([]) @@ -70,29 +102,7 @@ test "should return redirect for unauthed user when target is local AP ID for us 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") - Tesla.Mock.mock(fn - %{method: :get, url: "https://mastodon.social/users/emelie/statuses/101849165031453009"} -> - %Tesla.Env{ - status: 200, - headers: [{"content-type", "application/activity+json"}], - body: File.read!("test/fixtures/tesla_mock/status.emelie.json") - } - %{method: :get, url: "https://mastodon.social/users/emelie"} -> - %Tesla.Env{ - status: 200, - headers: [{"content-type", "application/activity+json"}], - body: File.read!("test/fixtures/tesla_mock/emelie.json") - } - %{method: :get, url: "https://mastodon.social/users/emelie/collections/featured"} -> - %Tesla.Env{ - status: 200, - headers: [{"content-type", "application/activity+json"}], - body: - File.read!("test/fixtures/users_mock/masto_featured.json") - |> String.replace("{{domain}}", "mastodon.social") - |> String.replace("{{nickname}}", "emelie") - } - end) + clear_config([Pleroma.Web.Endpoint, :url, :host], "sub.example.com") %{conn: conn} = oauth_access(["read:search"]) @@ -119,6 +129,24 @@ test "should return redirect for authed user when target is AP ID for user" do assert resp =~ "" end + test "should return redirect for authed user when target is URL for user" do + %{conn: conn} = oauth_access(["read:search"]) + remote_user = insert(:user, %{ + nickname: "emelie@mastodon.social", + local: false, + ap_id: "https://mastodon.social/users/emelie", + uri: "https://mastodon.social/@emelie", + }) + + resp = + conn + |> get("/api/v1/akkoma/protocol-handler?target=web%2Bap%3A%2F%2Fmastodon.social/%40emelie") + |> html_response(302) + + assert resp =~ "You are being" + assert resp =~ "" + end + test "should return redirect for authed user when target is AP ID for user, stripping userinfo" do %{conn: conn} = oauth_access(["read:search"]) remote_user = insert(:user, %{nickname: "akkoma@ihatebeinga.live", local: false, ap_id: "https://ihatebeinga.live/users/akkoma"}) @@ -133,29 +161,6 @@ test "should return redirect for authed user when target is AP ID for user, stri end test "should return redirect for authed user when target is AP ID for note activity" do - Tesla.Mock.mock(fn - %{method: :get, url: "https://mastodon.social/users/emelie/statuses/101849165031453009"} -> - %Tesla.Env{ - status: 200, - headers: [{"content-type", "application/activity+json"}], - body: File.read!("test/fixtures/tesla_mock/status.emelie.json") - } - %{method: :get, url: "https://mastodon.social/users/emelie"} -> - %Tesla.Env{ - status: 200, - headers: [{"content-type", "application/activity+json"}], - body: File.read!("test/fixtures/tesla_mock/emelie.json") - } - %{method: :get, url: "https://mastodon.social/users/emelie/collections/featured"} -> - %Tesla.Env{ - status: 200, - headers: [{"content-type", "application/activity+json"}], - body: - File.read!("test/fixtures/users_mock/masto_featured.json") - |> String.replace("{{domain}}", "mastodon.social") - |> String.replace("{{nickname}}", "emelie") - } - end) %{conn: conn} = oauth_access(["read:search"]) resp =