Merge branch 'bugfix/hubzilla-compat' into 'develop'

hubzilla compatibility

Closes #100

See merge request pleroma/pleroma!160
This commit is contained in:
lambda 2018-05-19 09:17:04 +00:00
commit 125d934bc6
11 changed files with 126 additions and 14 deletions

View file

@ -1,5 +1,6 @@
defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
alias Pleroma.Web.HTTPSignatures alias Pleroma.Web.HTTPSignatures
alias Pleroma.Web.ActivityPub.Utils
import Plug.Conn import Plug.Conn
require Logger require Logger
@ -12,7 +13,7 @@ def call(%{assigns: %{valid_signature: true}} = conn, _opts) do
end end
def call(conn, _opts) do def call(conn, _opts) do
user = conn.params["actor"] user = Utils.normalize_actor(conn.params["actor"])
Logger.debug("Checking sig for #{user}") Logger.debug("Checking sig for #{user}")
[signature | _] = get_req_header(conn, "signature") [signature | _] = get_req_header(conn, "signature")

View file

@ -404,18 +404,22 @@ def search(query, resolve) do
from( from(
u in User, u in User,
select_merge: %{ select_merge: %{
search_distance: fragment( search_distance:
"? <-> (? || ?)", fragment(
^query, "? <-> (? || ?)",
u.nickname, ^query,
u.name u.nickname,
)} u.name
)
}
) )
q = from(s in subquery(inner), q =
order_by: s.search_distance, from(
limit: 20 s in subquery(inner),
) order_by: s.search_distance,
limit: 20
)
Repo.all(q) Repo.all(q)
end end

View file

@ -401,6 +401,8 @@ def user_data_from_user_object(data) do
"url" => [%{"href" => data["image"]["url"]}] "url" => [%{"href" => data["image"]["url"]}]
} }
data = Transmogrifier.maybe_fix_user_object(data)
user_data = %{ user_data = %{
ap_id: data["id"], ap_id: data["id"],
info: %{ info: %{

View file

@ -495,4 +495,17 @@ def maybe_retire_websub(ap_id) do
Repo.delete_all(q) Repo.delete_all(q)
end end
end end
def maybe_fix_user_url(data) do
if is_map(data["url"]) do
data = Map.put(data, "url", data["url"]["href"])
end
data
end
def maybe_fix_user_object(data) do
data
|> maybe_fix_user_url
end
end end

View file

@ -5,6 +5,22 @@ defmodule Pleroma.Web.ActivityPub.Utils do
alias Ecto.{Changeset, UUID} alias Ecto.{Changeset, UUID}
import Ecto.Query import Ecto.Query
# Some implementations send the actor URI as the actor field, others send the entire actor object,
# so figure out what the actor's URI is based on what we have.
def normalize_actor(actor) do
cond do
is_binary(actor) ->
actor
is_map(actor) ->
actor["id"]
end
end
def normalize_params(params) do
Map.put(params, "actor", normalize_actor(params["actor"]))
end
def make_json_ld_header do def make_json_ld_header do
%{ %{
"@context" => [ "@context" => [

View file

@ -5,6 +5,7 @@ defmodule Pleroma.Web.Federator do
alias Pleroma.Web.{WebFinger, Websub} alias Pleroma.Web.{WebFinger, Websub}
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ActivityPub.Utils
require Logger require Logger
@websub Application.get_env(:pleroma, :websub) @websub Application.get_env(:pleroma, :websub)
@ -91,6 +92,8 @@ def handle(:incoming_doc, doc) do
def handle(:incoming_ap_doc, params) do def handle(:incoming_ap_doc, params) do
Logger.info("Handling incoming AP activity") Logger.info("Handling incoming AP activity")
params = Utils.normalize_params(params)
with {:ok, _user} <- ap_enabled_actor(params["actor"]), with {:ok, _user} <- ap_enabled_actor(params["actor"]),
nil <- Activity.get_by_ap_id(params["id"]), nil <- Activity.get_by_ap_id(params["id"]),
{:ok, _activity} <- Transmogrifier.handle_incoming(params) do {:ok, _activity} <- Transmogrifier.handle_incoming(params) do

View file

@ -1,7 +1,7 @@
# https://tools.ietf.org/html/draft-cavage-http-signatures-08 # https://tools.ietf.org/html/draft-cavage-http-signatures-08
defmodule Pleroma.Web.HTTPSignatures do defmodule Pleroma.Web.HTTPSignatures do
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Utils
require Logger require Logger
def split_signature(sig) do def split_signature(sig) do
@ -31,14 +31,14 @@ def validate(headers, signature, public_key) do
def validate_conn(conn) do def validate_conn(conn) do
# TODO: How to get the right key and see if it is actually valid for that request. # TODO: How to get the right key and see if it is actually valid for that request.
# For now, fetch the key for the actor. # For now, fetch the key for the actor.
with actor_id <- conn.params["actor"], with actor_id <- Utils.normalize_actor(conn.params["actor"]),
{:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do {:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do
if validate_conn(conn, public_key) do if validate_conn(conn, public_key) do
true true
else else
Logger.debug("Could not validate, re-fetching user and trying one more time") Logger.debug("Could not validate, re-fetching user and trying one more time")
# Fetch user anew and try one more time # Fetch user anew and try one more time
with actor_id <- conn.params["actor"], with actor_id <- Utils.normalize_actor(conn.params["actor"]),
{:ok, _user} <- ActivityPub.make_user_from_ap_id(actor_id), {:ok, _user} <- ActivityPub.make_user_from_ap_id(actor_id),
{:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do {:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do
validate_conn(conn, public_key) validate_conn(conn, public_key)

View file

@ -0,0 +1 @@
{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1","https://hubzilla.example.org/apschema/v1.2"],"type":"Person","id":"https://hubzilla.example.org/channel/kaniini","preferredUsername":"kaniini","name":"kaniini","icon":{"type":"Image","mediaType":"image/jpeg","url":"https://hubzilla.example.org/photo/profile/l/281","height":300,"width":300},"url":{"type":"Link","mediaType":"text/html","href":"https://hubzilla.example.org/channel/kaniini"},"inbox":"https://hubzilla.example.org/inbox/kaniini","outbox":"https://hubzilla.example.org/outbox/kaniini","followers":"https://hubzilla.example.org/followers/kaniini","following":"https://hubzilla.example.org/following/kaniini","endpoints":{"sharedInbox":"https://hubzilla.example.org/inbox"},"publicKey":{"id":"https://hubzilla.example.org/channel/kaniini/public_key_pem","owner":"https://hubzilla.example.org/channel/kaniini","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvXCDkQPw+1N8B2CUd5s2\nbYvjHt+t7soMNfUiRy0qGbgW46S45k5lCq1KpbFIX3sgGZ4OWjnXVbvjCJi4kl5M\nfm5DBXzpuu05AmjVl8hqk4GejajiE/1Nq0uWHPiOSFWispUjCzzCu65V+IsiE5JU\nvcL6WEf/pYNRq7gYqyT693F7+cO5/rVv9OScx5UOxbIuU1VXYhdHCqAMDJWadC89\nhePrcD3HOQKl06W2tDxHcWk6QjrdsUQGbNOgK/QIN9gSxA+rCFEvH5O0HAhI0aXq\ncOB+vysJUFLeQOAqmAKvKS5V6RqE1GqqT0pDWHack4EmQi0gkgVzo+45xoP6wfDl\nWwG88w21LNxGvGHuN4I8mg6cEoApqKQBSOj086UtfDfSlPC1B+PRD2phE5etucHd\nF/RIWN3SxVzU9BKIiaDm2gwOpvI8QuorQb6HDtZFO5NsSN3PnMnSywPe7kXl/469\nuQRYXrseqyOVIi6WjhvXkyWVKVE5CBz+S8wXHfKph+9YOyUcJeAVMijp9wrjBlMc\noSzOGu79oM7tpMSq/Xo6ePJ/glNOwZR+OKrg92Qp9BGTKDNwGrxuxP/9KwWtGLNf\nOMTtIkxtC3ubhxL3lBxOd7l+Bmum0UJV2f8ogkCgvTpIz05jMoyU8qWl6kkWNQlY\nDropXWaOfy7Lac+G4qlfSgsCAwEAAQ==\n-----END PUBLIC KEY-----\n"},"nomadicLocations":[{"id":"https://hubzilla.example.org/locs/kaniini","type":"nomadicLocation","locationAddress":"acct:kaniini@hubzilla.example.org","locationPrimary":true,"locationDeleted":false}],"signature":{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1"],"type":"RsaSignature2017","nonce":"6b981a2f3bdcffc20252e3b131d4a4569fd2dea9fac543e5196136302f492694","creator":"https://hubzilla.example.org/channel/kaniini/public_key_pem","created":"2018-05-19T08:19:13Z","signatureValue":"ezpT4iCIUzJSeJa/Jsf4EkgbX9enWZG/0eliLXZcvkeCX9mZabaX9LMQRViP2GSlAJBHJu+UqK5LWaoWw9pYkQQHUL+43w2DeBxQicEcPqpT46j6pHuWptfwB8YHTC2/Pb56Y/jseU37j+FW8xVmcGZk4cPqJRLQNojwJlQiFOpBEd4Cel6081W12Pep578+6xBL+h92RJsWznA1gE/NV9dkCqoAoNdiORJg68sVTm0yYxPit2D/DLwXUFeBhC47EZtY3DtAOf7rADGwbquXKug/wtEI47R4p9dJvMWERSVW9O2FmDk8deUjRR3qO1iYGce8O+uMnnBHmuTcToRUHH7mxfMdqjfbcZ9DGBjKtLPSOyVPT9rENeyX8fsksmX0XhfHsNSWkmeDaU5/Au3IY75gDewiGzmzLOpRc6GUnHHro7lMpyMuo3lLZKjNVsFZbx+sXCYwORz5GAMuwIt/iCUdrsQsF5aycqfUAZrFBPguH6DVjbMUqyLvS78sDKiWqgWVhq9VDKse+WuQaJLGBDJNF9APoA6NDMjjIBZfmkGf2mV7ubIYihoOncUjahFqxU5306cNxAcdj2uNcwkgX4BCnBe/L2YsvMHhZrupzDewWWy4fxhktyoZ7VhLSl1I7fMPytjOpb9EIvng4DHGX2t+hKfon2rCGfECPavwiTM="}}

View file

@ -0,0 +1,31 @@
{
"type": "Follow",
"signature": {
"type": "RsaSignature2017",
"signatureValue": "Kn1/UkAQGJVaXBfWLAHcnwHg8YMAUqlEaBuYLazAG+pz5hqivsyrBmPV186Xzr+B4ZLExA9+SnOoNx/GOz4hBm0kAmukNSILAsUd84tcJ2yT9zc1RKtembK4WiwOw7li0+maeDN0HaB6t+6eTqsCWmtiZpprhXD8V1GGT8yG7X24fQ9oFGn+ng7lasbcCC0988Y1eGqNe7KryxcPuQz57YkDapvtONzk8gyLTkZMV4De93MyRHq6GVjQVIgtiYabQAxrX6Q8C+4P/jQoqdWJHEe+MY5JKyNaT/hMPt2Md1ok9fZQBGHlErk22/zy8bSN19GdG09HmIysBUHRYpBLig==",
"creator": "https://hubzilla.example.org/channel/kaniini#main-key",
"created": "2018-02-17T13:29:31Z"
},
"object": "https://localtesting.pleroma.lol/users/lain",
"nickname": "lain",
"id": "https://hubzilla.example.org/channel/kaniini#follows/2",
"actor": {
"id": "https://hubzilla.example.org/channel/kaniini"
},
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"toot": "http://joinmastodon.org/ns#",
"sensitive": "as:sensitive",
"ostatus": "http://ostatus.org#",
"movedTo": "as:movedTo",
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
"conversation": "ostatus:conversation",
"atomUri": "ostatus:atomUri",
"Hashtag": "as:Hashtag",
"Emoji": "toot:Emoji"
}
]
}

View file

@ -628,6 +628,18 @@ def get("http://mastodon.example.org/users/admin", [Accept: "application/activit
}} }}
end end
def get(
"https://hubzilla.example.org/channel/kaniini",
[Accept: "application/activity+json"],
_
) do
{:ok,
%Response{
status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/kaniini@hubzilla.example.org.json")
}}
end
def get("https://masto.quad.moe/users/_HellPie", [Accept: "application/activity+json"], _) do def get("https://masto.quad.moe/users/_HellPie", [Accept: "application/activity+json"], _) do
{:ok, {:ok,
%Response{ %Response{

View file

@ -1,6 +1,7 @@
defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
use Pleroma.DataCase use Pleroma.DataCase
alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.OStatus alias Pleroma.Web.OStatus
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.User alias Pleroma.User
@ -118,6 +119,23 @@ test "it works for incoming follow requests" do
assert User.following?(User.get_by_ap_id(data["actor"]), user) assert User.following?(User.get_by_ap_id(data["actor"]), user)
end end
test "it works for incoming follow requests from hubzilla" do
user = insert(:user)
data =
File.read!("test/fixtures/hubzilla-follow-activity.json")
|> Poison.decode!()
|> Map.put("object", user.ap_id)
|> Utils.normalize_params()
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
assert data["actor"] == "https://hubzilla.example.org/channel/kaniini"
assert data["type"] == "Follow"
assert data["id"] == "https://hubzilla.example.org/channel/kaniini#follows/2"
assert User.following?(User.get_by_ap_id(data["actor"]), user)
end
test "it works for incoming likes" do test "it works for incoming likes" do
user = insert(:user) user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "hello"}) {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
@ -420,4 +438,15 @@ test "it deletes all websub client subscripitions with the user as topic" do
assert Repo.get(WebsubClientSubscription, ws2.id) assert Repo.get(WebsubClientSubscription, ws2.id)
end end
end end
describe "actor rewriting" do
test "it fixes the actor URL property to be a proper URI" do
data = %{
"url" => %{"href" => "http://example.com"}
}
rewritten = Transmogrifier.maybe_fix_user_object(data)
assert rewritten["url"] == "http://example.com"
end
end
end end