Rework remote user subscription.
This commit is contained in:
parent
69922bc724
commit
427bac0966
|
@ -37,8 +37,8 @@ def handle_incoming(xml_string) do
|
||||||
def handle_note(doc) do
|
def handle_note(doc) do
|
||||||
content_html = string_from_xpath("/entry/content[1]", doc)
|
content_html = string_from_xpath("/entry/content[1]", doc)
|
||||||
|
|
||||||
[author] = :xmerl_xpath.string('/entry/author[1]', doc)
|
uri = string_from_xpath("/entry/author/uri[1]", doc)
|
||||||
{:ok, actor} = find_or_make_user(author)
|
{:ok, actor} = find_or_make_user(uri)
|
||||||
|
|
||||||
context = string_from_xpath("/entry/ostatus:conversation[1]", doc) |> String.trim
|
context = string_from_xpath("/entry/ostatus:conversation[1]", doc) |> String.trim
|
||||||
context = if String.length(context) > 0 do
|
context = if String.length(context) > 0 do
|
||||||
|
@ -78,43 +78,31 @@ def handle_note(doc) do
|
||||||
ActivityPub.create(to, actor, context, object, %{}, date)
|
ActivityPub.create(to, actor, context, object, %{}, date)
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_or_make_user(author_doc) do
|
def find_or_make_user(uri) do
|
||||||
{:xmlObj, :string, uri } = :xmerl_xpath.string('string(/author[1]/uri)', author_doc)
|
|
||||||
|
|
||||||
query = from user in User,
|
query = from user in User,
|
||||||
where: user.local == false and fragment("? @> ?", user.info, ^%{ostatus_uri: to_string(uri)})
|
where: user.local == false and fragment("? @> ?", user.info, ^%{uri: uri})
|
||||||
|
|
||||||
user = Repo.one(query)
|
user = Repo.one(query)
|
||||||
|
|
||||||
if is_nil(user) do
|
if is_nil(user) do
|
||||||
make_user(author_doc)
|
make_user(uri)
|
||||||
else
|
else
|
||||||
{:ok, user}
|
{:ok, user}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_user(author_doc) do
|
def make_user(uri) do
|
||||||
author = string_from_xpath("/author[1]/uri", author_doc)
|
with {:ok, info} <- gather_user_info(uri) do
|
||||||
name = string_from_xpath("/author[1]/name", author_doc)
|
|
||||||
preferredUsername = string_from_xpath("/author[1]/poco:preferredUsername", author_doc)
|
|
||||||
displayName = string_from_xpath("/author[1]/poco:displayName", author_doc)
|
|
||||||
avatar = make_avatar_object(author_doc)
|
|
||||||
|
|
||||||
data = %{
|
data = %{
|
||||||
local: false,
|
local: false,
|
||||||
name: preferredUsername || name,
|
name: info.name,
|
||||||
nickname: displayName || name,
|
nickname: info.nickname,
|
||||||
ap_id: author,
|
ap_id: info.uri,
|
||||||
info: %{
|
info: info
|
||||||
"ostatus_uri" => author,
|
|
||||||
"host" => URI.parse(author).host,
|
|
||||||
"system" => "ostatus"
|
|
||||||
},
|
|
||||||
avatar: avatar
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Repo.insert(Ecto.Changeset.change(%User{}, data))
|
Repo.insert(Ecto.Changeset.change(%User{}, data))
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# TODO: Just takes the first one for now.
|
# TODO: Just takes the first one for now.
|
||||||
defp make_avatar_object(author_doc) do
|
defp make_avatar_object(author_doc) do
|
||||||
|
|
|
@ -42,7 +42,7 @@ def represent_user(user) do
|
||||||
|
|
||||||
# FIXME: Make this call the host-meta to find the actual address.
|
# FIXME: Make this call the host-meta to find the actual address.
|
||||||
defp webfinger_address(domain) do
|
defp webfinger_address(domain) do
|
||||||
"https://#{domain}/.well-known/webfinger"
|
"//#{domain}/.well-known/webfinger"
|
||||||
end
|
end
|
||||||
|
|
||||||
defp webfinger_from_xml(doc) do
|
defp webfinger_from_xml(doc) do
|
||||||
|
@ -61,9 +61,21 @@ defp webfinger_from_xml(doc) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def finger(account, getter \\ &HTTPoison.get/3) do
|
def finger(account, getter \\ &HTTPoison.get/3) do
|
||||||
[name, domain] = String.split(account, "@")
|
domain = with [_name, domain] <- String.split(account, "@") do
|
||||||
|
domain
|
||||||
|
else _e ->
|
||||||
|
URI.parse(account).host
|
||||||
|
end
|
||||||
address = webfinger_address(domain)
|
address = webfinger_address(domain)
|
||||||
with {:ok, %{status_code: status_code, body: body}} when status_code in 200..299 <- getter.(address, ["Accept": "application/xrd+xml"], [params: [resource: account]]),
|
|
||||||
|
# try https first
|
||||||
|
response = with {:ok, result} <- getter.("https:" <> address, ["Accept": "application/xrd+xml"], [params: [resource: account]]) do
|
||||||
|
{:ok, result}
|
||||||
|
else _ ->
|
||||||
|
getter.("http:" <> address, ["Accept": "application/xrd+xml"], [params: [resource: account]])
|
||||||
|
end
|
||||||
|
|
||||||
|
with {:ok, %{status_code: status_code, body: body}} when status_code in 200..299 <- response,
|
||||||
doc <- XML.parse_document(body),
|
doc <- XML.parse_document(body),
|
||||||
{:ok, data} <- webfinger_from_xml(doc) do
|
{:ok, data} <- webfinger_from_xml(doc) do
|
||||||
{:ok, data}
|
{:ok, data}
|
||||||
|
|
|
@ -102,19 +102,21 @@ defp valid_topic(%{"hub.topic" => topic}, user) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def subscribe(user, topic, requester \\ &request_subscription/1) do
|
def subscribe(subscriber, subscribed, requester \\ &request_subscription/1) do
|
||||||
|
topic = subscribed.info["topic"]
|
||||||
# FIXME: Race condition, use transactions
|
# FIXME: Race condition, use transactions
|
||||||
{:ok, subscription} = with subscription when not is_nil(subscription) <- Repo.get_by(WebsubClientSubscription, topic: topic) do
|
{:ok, subscription} = with subscription when not is_nil(subscription) <- Repo.get_by(WebsubClientSubscription, topic: topic) do
|
||||||
subscribers = [user.ap_id, subscription.subcribers] |> Enum.uniq
|
subscribers = [subscriber.ap_id, subscription.subscribers] |> Enum.uniq
|
||||||
change = Ecto.Changeset.change(subscription, %{subscribers: subscribers})
|
change = Ecto.Changeset.change(subscription, %{subscribers: subscribers})
|
||||||
Repo.update(change)
|
Repo.update(change)
|
||||||
else _e ->
|
else _e ->
|
||||||
subscription = %WebsubClientSubscription{
|
subscription = %WebsubClientSubscription{
|
||||||
topic: topic,
|
topic: topic,
|
||||||
subscribers: [user.ap_id],
|
hub: subscribed.info["hub"],
|
||||||
|
subscribers: [subscriber.ap_id],
|
||||||
state: "requested",
|
state: "requested",
|
||||||
secret: :crypto.strong_rand_bytes(8) |> Base.url_encode64,
|
secret: :crypto.strong_rand_bytes(8) |> Base.url_encode64,
|
||||||
user: user
|
user: subscribed
|
||||||
}
|
}
|
||||||
Repo.insert(subscription)
|
Repo.insert(subscription)
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,40 +25,20 @@ test "handle incoming replies" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "new remote user creation" do
|
describe "new remote user creation" do
|
||||||
test "make new user or find them based on an 'author' xml doc" do
|
|
||||||
incoming = File.read!("test/fixtures/user_name_only.xml")
|
|
||||||
doc = XML.parse_document(incoming)
|
|
||||||
|
|
||||||
{:ok, user} = OStatus.find_or_make_user(doc)
|
|
||||||
|
|
||||||
assert user.name == "lambda"
|
|
||||||
assert user.nickname == "lambda"
|
|
||||||
assert user.local == false
|
|
||||||
assert user.info["ostatus_uri"] == "http://gs.example.org:4040/index.php/user/1"
|
|
||||||
assert user.info["system"] == "ostatus"
|
|
||||||
assert user.ap_id == "http://gs.example.org:4040/index.php/user/1"
|
|
||||||
|
|
||||||
{:ok, user_again} = OStatus.find_or_make_user(doc)
|
|
||||||
|
|
||||||
assert user == user_again
|
|
||||||
end
|
|
||||||
|
|
||||||
test "tries to use the information in poco fields" do
|
test "tries to use the information in poco fields" do
|
||||||
incoming = File.read!("test/fixtures/user_full.xml")
|
# TODO make test local
|
||||||
doc = XML.parse_document(incoming)
|
uri = "https://social.heldscal.la/user/23211"
|
||||||
|
|
||||||
{:ok, user} = OStatus.find_or_make_user(doc)
|
{:ok, user} = OStatus.find_or_make_user(uri)
|
||||||
|
|
||||||
|
user = Repo.get(Pleroma.User, user.id)
|
||||||
assert user.name == "Constance Variable"
|
assert user.name == "Constance Variable"
|
||||||
assert user.nickname == "lambadalambda"
|
assert user.nickname == "lambadalambda"
|
||||||
assert user.local == false
|
assert user.local == false
|
||||||
assert user.info["ostatus_uri"] == "http://gs.example.org:4040/index.php/user/1"
|
assert user.info["uri"] == uri
|
||||||
assert user.info["system"] == "ostatus"
|
assert user.ap_id == uri
|
||||||
assert user.ap_id == "http://gs.example.org:4040/index.php/user/1"
|
|
||||||
|
|
||||||
assert List.first(user.avatar["url"])["href"] == "http://gs.example.org:4040/theme/neo-gnu/default-avatar-profile.png"
|
{:ok, user_again} = OStatus.find_or_make_user(uri)
|
||||||
|
|
||||||
{:ok, user_again} = OStatus.find_or_make_user(doc)
|
|
||||||
|
|
||||||
assert user == user_again
|
assert user == user_again
|
||||||
end
|
end
|
||||||
|
@ -84,5 +64,25 @@ test "it returns user info in a hash" do
|
||||||
}
|
}
|
||||||
assert data == expected
|
assert data == expected
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it works with the uri" do
|
||||||
|
user = "https://social.heldscal.la/user/29191"
|
||||||
|
|
||||||
|
# TODO: make test local
|
||||||
|
{:ok, data} = OStatus.gather_user_info(user)
|
||||||
|
|
||||||
|
expected = %{
|
||||||
|
hub: "https://social.heldscal.la/main/push/hub",
|
||||||
|
magic_key: "RSA.wQ3i9UA0qmAxZ0WTIp4a-waZn_17Ez1pEEmqmqoooRsG1_BvpmOvLN0G2tEcWWxl2KOtdQMCiPptmQObeZeuj48mdsDZ4ArQinexY2hCCTcbV8Xpswpkb8K05RcKipdg07pnI7tAgQ0VWSZDImncL6YUGlG5YN8b5TjGOwk2VG8=.AQAB",
|
||||||
|
name: "shp",
|
||||||
|
nickname: "shp",
|
||||||
|
salmon: "https://social.heldscal.la/main/salmon/user/29191",
|
||||||
|
subject: "https://social.heldscal.la/user/29191",
|
||||||
|
topic: "https://social.heldscal.la/api/statuses/user_timeline/29191.atom",
|
||||||
|
uri: "https://social.heldscal.la/user/29191",
|
||||||
|
fqn: user
|
||||||
|
}
|
||||||
|
assert data == expected
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -93,12 +93,13 @@ def accepting_verifier(subscription) do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "initiate a subscription for a given user and topic" do
|
test "initiate a subscription for a given user and topic" do
|
||||||
user = insert(:user)
|
subscriber = insert(:user)
|
||||||
topic = "http://example.org/some-topic.atom"
|
user = insert(:user, %{info: %{ "topic" => "some_topic", "hub" => "some_hub"}})
|
||||||
|
|
||||||
{:ok, websub} = Websub.subscribe(user, topic, &accepting_verifier/1)
|
{:ok, websub} = Websub.subscribe(subscriber, user, &accepting_verifier/1)
|
||||||
assert websub.subscribers == [user.ap_id]
|
assert websub.subscribers == [subscriber.ap_id]
|
||||||
assert websub.topic == topic
|
assert websub.topic == "some_topic"
|
||||||
|
assert websub.hub == "some_hub"
|
||||||
assert is_binary(websub.secret)
|
assert is_binary(websub.secret)
|
||||||
assert websub.user == user
|
assert websub.user == user
|
||||||
assert websub.state == "accepted"
|
assert websub.state == "accepted"
|
||||||
|
|
Loading…
Reference in a new issue