Merge pull request #74 from SuperTux88/parse-rfc-7033-webfinger
Parse RFC 7033 webfinger
This commit is contained in:
commit
0b07b36017
8 changed files with 425 additions and 202 deletions
|
|
@ -2,88 +2,41 @@
|
|||
title: WebFinger
|
||||
---
|
||||
|
||||
diaspora\* uses an old draft of [WebFinger][webfinger-draft] to discover users from other pods.
|
||||
|
||||
{% include warning_box.html
|
||||
title="Old WebFinger"
|
||||
content="<p>diaspora* doesn't yet fully support the RFC 7033 WebFinger!</p>"
|
||||
%}
|
||||
|
||||
## WebFinger endpoint discovery
|
||||
|
||||
To find the WebFinger endpoint, the requesting server must get the [host metadata][host-meta] information for the
|
||||
domain of the searched diaspora\* ID.
|
||||
|
||||
### Request
|
||||
|
||||
Let's assume we are searching for `alice@example.org`, then we need the host metadata for `example.org`.
|
||||
|
||||
The request should first be tried with https, if this doesn't work, the requesting server should fallback to http.
|
||||
|
||||
If the server response indicates that the host-meta resource is located elsewhere (a 301, 302, or 307 response status
|
||||
code), the requesting server should try to obtain the resource from the location provided in the response.
|
||||
|
||||
#### Example
|
||||
|
||||
~~~
|
||||
GET /.well-known/host-meta
|
||||
Host: example.org
|
||||
~~~
|
||||
|
||||
### Response
|
||||
|
||||
The host-meta document must be in the [XRD 1.0 document format][xrd] and should be served with the
|
||||
`application/xrd+xml` media type.
|
||||
|
||||
#### Mandatory Link Relations
|
||||
|
||||
The Host Metadata response must contain the following link relations:
|
||||
|
||||
| Link Relation | Type | Description |
|
||||
| ------------- | ------------ | ------------------------------------------------------------------------- |
|
||||
| lrdd | url template | The template to the URL where the server provides the WebFinger endpoint. |
|
||||
|
||||
The Host Metadata response may contain other optional link relations.
|
||||
|
||||
#### Example
|
||||
|
||||
~~~
|
||||
Status: 200 OK
|
||||
Content-Type: application/xrd+xml; charset=utf-8
|
||||
~~~
|
||||
~~~xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
|
||||
<Link rel="lrdd" type="application/xrd+xml" template="https://example.org/.well-known/webfinger.xml?resource={uri}"/>
|
||||
</XRD>
|
||||
~~~
|
||||
diaspora\* uses [WebFinger][webfinger-rfc] to discover users from other pods.
|
||||
|
||||
## WebFinger
|
||||
|
||||
### Request
|
||||
|
||||
~~~
|
||||
GET /.well-known/webfinger
|
||||
~~~
|
||||
|
||||
Let's assume we are searching for `alice@example.org`, then we need to make a request to `example.org`.
|
||||
|
||||
#### Parameters
|
||||
|
||||
The requesting server must replace the ``{uri}`` in the lrdd-template from the host-meta request with the diaspora\* ID
|
||||
of the searched person.
|
||||
| Name | Description |
|
||||
| ---------- | ------------------------------------------------------------ |
|
||||
| `resource` | The "acct" URI for the diaspora\* ID of the searched person. |
|
||||
|
||||
#### Example
|
||||
|
||||
~~~
|
||||
GET /.well-known/webfinger.xml?resource=acct:alice@example.org
|
||||
GET /.well-known/webfinger?resource=acct:alice@example.org
|
||||
Host: example.org
|
||||
~~~
|
||||
|
||||
### Response
|
||||
|
||||
The webfinger document must be in the [XRD 1.0 document format][xrd] and should be served with the
|
||||
`application/xrd+xml` media type.
|
||||
The WebFinger document must be in the [JSON Resource Descriptor (JRD) format][jrd] and should be served with the
|
||||
`application/jrd+json` media type.
|
||||
|
||||
If the requested diaspora\* ID is unknown by the server, it must return a 404 status code.
|
||||
|
||||
#### Subject
|
||||
|
||||
The ``Subject`` element should contain the webfinger address that was asked for. If it does not, then this webfinger
|
||||
The `subject` element should contain the WebFinger address that was asked for. If it does not, then this WebFinger
|
||||
profile must be ignored.
|
||||
|
||||
#### Mandatory Link Relations
|
||||
|
|
@ -101,26 +54,32 @@ The WebFinger response may contain other optional link relations.
|
|||
|
||||
~~~
|
||||
Status: 200 OK
|
||||
Content-Type: application/xrd+xml; charset=utf-8
|
||||
Content-Type: application/jrd+json; charset=utf-8
|
||||
Access-Control-Allow-Origin: *
|
||||
~~~
|
||||
~~~xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
|
||||
<Subject>acct:alice@example.org</Subject>
|
||||
<Link rel="http://microformats.org/profile/hcard" type="text/html" href="https://example.org/hcard/users/7dba7ca01d64013485eb3131731751e9"/>
|
||||
<Link rel="http://joindiaspora.com/seed_location" type="text/html" href="https://example.org/"/>
|
||||
</XRD>
|
||||
~~~json
|
||||
{
|
||||
"subject": "acct:alice@example.org",
|
||||
"links": [
|
||||
{
|
||||
"rel": "http://microformats.org/profile/hcard",
|
||||
"type": "text/html",
|
||||
"href": "https://example.org/hcard/users/7dba7ca01d64013485eb3131731751e9"
|
||||
},
|
||||
{
|
||||
"rel": "http://joindiaspora.com/seed_location",
|
||||
"type": "text/html",
|
||||
"href": "https://example.org/"
|
||||
}
|
||||
]
|
||||
}
|
||||
~~~
|
||||
|
||||
## Additional information and specifications
|
||||
|
||||
* [RFC 6415: Web Host Metadata][host-meta]
|
||||
* [WebFinger draft][webfinger-draft]
|
||||
* [Extensible Resource Descriptor (XRD) Version 1.0][xrd]
|
||||
* [RFC 7033: WebFinger][webfinger-rfc]
|
||||
* [JSON Resource Descriptor (JRD)][jrd]
|
||||
|
||||
[host-meta]: https://tools.ietf.org/html/rfc6415
|
||||
[webfinger-draft]: https://tools.ietf.org/html/draft-jones-appsawg-webfinger-06
|
||||
[webfinger-rfc]: https://tools.ietf.org/html/rfc7033
|
||||
[xrd]: http://docs.oasis-open.org/xri/xrd/v1.0/xrd-1.0.html
|
||||
[jrd]: https://www.packetizer.com/json/jrd/
|
||||
[hcard]: {{ site.baseurl }}/discovery/hcard.html
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ module DiasporaFederation
|
|||
]
|
||||
|
||||
# defaults
|
||||
@webfinger_http_fallback = false
|
||||
@http_concurrency = 20
|
||||
@http_timeout = 30
|
||||
@http_verbose = false
|
||||
|
|
@ -74,6 +75,21 @@ module DiasporaFederation
|
|||
# @param [String] value path to certificate authorities
|
||||
attr_accessor :certificate_authorities
|
||||
|
||||
# Configure if WebFinger discovery should fallback to http if https fails (default: +false+)
|
||||
#
|
||||
# This is useful for example for development environments where https isn't available.
|
||||
#
|
||||
# This setting only applies to the WebFinger route from RFC 7033 +/.well-known/webfinger+.
|
||||
# Legacy WebFinger flow unconditionally falls back to http.
|
||||
#
|
||||
# @overload webfinger_http_fallback
|
||||
# @return [Boolean] webfinger http fallback enabled
|
||||
# @overload webfinger_http_fallback=
|
||||
# @example
|
||||
# config.webfinger_http_fallback = AppConfig.server.rails_environment == "development"
|
||||
# @param [Boolean] value webfinger http fallback enabled
|
||||
attr_accessor :webfinger_http_fallback
|
||||
|
||||
# Maximum number of parallel HTTP requests made to other pods (default: +20+)
|
||||
#
|
||||
# @overload http_concurrency
|
||||
|
|
|
|||
|
|
@ -58,23 +58,33 @@ module DiasporaFederation
|
|||
retry
|
||||
end
|
||||
|
||||
def host_meta_url
|
||||
domain = diaspora_id.split("@")[1]
|
||||
"https://#{domain}/.well-known/host-meta"
|
||||
def domain
|
||||
@domain ||= diaspora_id.split("@")[1]
|
||||
end
|
||||
|
||||
def acct_parameter
|
||||
"acct:#{diaspora_id}"
|
||||
end
|
||||
|
||||
def legacy_webfinger_url_from_host_meta
|
||||
# This tries the xrd url with https first, then falls back to http.
|
||||
host_meta = HostMeta.from_xml get(host_meta_url, true)
|
||||
host_meta.webfinger_template_url.gsub("{uri}", "acct:#{diaspora_id}")
|
||||
host_meta = HostMeta.from_xml(get("https://#{domain}/.well-known/host-meta", true))
|
||||
host_meta.webfinger_template_url.gsub("{uri}", acct_parameter)
|
||||
end
|
||||
|
||||
def webfinger
|
||||
@webfinger ||= WebFinger.from_xml get(legacy_webfinger_url_from_host_meta)
|
||||
return @webfinger if @webfinger
|
||||
webfinger_url = "https://#{domain}/.well-known/webfinger?resource=#{acct_parameter}"
|
||||
|
||||
# This tries the WebFinger URL with https first, then falls back to http if webfinger_http_fallback is enabled.
|
||||
@webfinger = WebFinger.from_json(get(webfinger_url, DiasporaFederation.webfinger_http_fallback))
|
||||
rescue => e
|
||||
logger.warn "WebFinger failed, retrying with legacy WebFinger for #{diaspora_id}: #{e.class}: #{e.message}"
|
||||
@webfinger = WebFinger.from_xml(get(legacy_webfinger_url_from_host_meta))
|
||||
end
|
||||
|
||||
def hcard
|
||||
@hcard ||= HCard.from_html get(webfinger.hcard_url)
|
||||
@hcard ||= HCard.from_html(get(webfinger.hcard_url))
|
||||
end
|
||||
|
||||
def person
|
||||
|
|
|
|||
|
|
@ -119,8 +119,20 @@ module DiasporaFederation
|
|||
# @return [WebFinger] WebFinger instance
|
||||
# @raise [InvalidData] if the given XML string is invalid or incomplete
|
||||
def self.from_xml(webfinger_xml)
|
||||
data = parse_xml_and_validate(webfinger_xml)
|
||||
from_hash(parse_xml_and_validate(webfinger_xml))
|
||||
end
|
||||
|
||||
# Creates a WebFinger instance from the given JSON string
|
||||
# @param [String] webfinger_json WebFinger JSON string
|
||||
# @return [WebFinger] WebFinger instance
|
||||
def self.from_json(webfinger_json)
|
||||
from_hash(XrdDocument.json_data(webfinger_json))
|
||||
end
|
||||
|
||||
# Creates a WebFinger instance from the given data
|
||||
# @param [Hash] data WebFinger data hash
|
||||
# @return [WebFinger] WebFinger instance
|
||||
def self.from_hash(data)
|
||||
links = data[:links]
|
||||
|
||||
new(
|
||||
|
|
|
|||
|
|
@ -83,10 +83,10 @@ module DiasporaFederation
|
|||
def to_json
|
||||
{
|
||||
subject: subject,
|
||||
expires: expires,
|
||||
expires: (expires.strftime(DATETIME_FORMAT) if expires.instance_of?(DateTime)),
|
||||
aliases: (aliases if aliases.any?),
|
||||
links: (links if links.any?),
|
||||
properties: (properties if properties.any?)
|
||||
properties: (properties if properties.any?),
|
||||
links: (links if links.any?)
|
||||
}.reject {|_, v| v.nil? }
|
||||
end
|
||||
|
||||
|
|
@ -115,6 +115,27 @@ module DiasporaFederation
|
|||
end
|
||||
end
|
||||
|
||||
# Parse the JRD document from the given string and create a hash containing
|
||||
# the extracted data with symbolized keys.
|
||||
#
|
||||
# @param [String] jrd_doc JSON string
|
||||
# @return [Hash] extracted data
|
||||
# @raise [InvalidDocument] if the JRD is malformed
|
||||
def self.json_data(jrd_doc)
|
||||
json_hash = JSON.parse(jrd_doc)
|
||||
|
||||
{
|
||||
subject: json_hash["subject"],
|
||||
expires: (DateTime.strptime(json_hash["expires"], DATETIME_FORMAT) if json_hash.key?("expires")),
|
||||
aliases: json_hash["aliases"],
|
||||
properties: json_hash["properties"],
|
||||
links: symbolize_keys_for_links(json_hash["links"])
|
||||
}.reject {|_, v| v.nil? }
|
||||
rescue JSON::JSONError => e
|
||||
raise InvalidDocument,
|
||||
"Not a JRD document: #{e.class}: #{e.message[0..255].encode(Encoding.default_external, undef: :replace)}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :expires
|
||||
|
|
@ -180,6 +201,17 @@ module DiasporaFederation
|
|||
end
|
||||
data[:links] = links unless links.empty?
|
||||
end
|
||||
|
||||
# symbolize link keys from JSON hash, but only convert known keys
|
||||
private_class_method def self.symbolize_keys_for_links(links)
|
||||
links.map do |link|
|
||||
{}.tap do |hash|
|
||||
LINK_ATTRS.each do |attr|
|
||||
hash[attr] = link[attr.to_s] if link.key?(attr.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
module DiasporaFederation
|
||||
describe Discovery::Discovery do
|
||||
let(:host_meta_xrd) { Discovery::HostMeta.from_base_url("http://localhost:3000/").to_xml }
|
||||
let(:webfinger_xrd) {
|
||||
DiasporaFederation::Discovery::WebFinger.new(
|
||||
let(:webfinger_data) {
|
||||
{
|
||||
acct_uri: "acct:#{alice.diaspora_id}",
|
||||
alias_url: alice.alias_url,
|
||||
hcard_url: alice.hcard_url,
|
||||
|
|
@ -13,7 +13,13 @@ module DiasporaFederation
|
|||
subscribe_url: alice.subscribe_url,
|
||||
guid: alice.guid,
|
||||
public_key: alice.serialized_public_key
|
||||
).to_xml
|
||||
}
|
||||
}
|
||||
let(:webfinger_xrd) {
|
||||
DiasporaFederation::Discovery::WebFinger.new(webfinger_data).to_xml
|
||||
}
|
||||
let(:webfinger_jrd) {
|
||||
JSON.pretty_generate(DiasporaFederation::Discovery::WebFinger.new(webfinger_data).to_json)
|
||||
}
|
||||
let(:hcard_html) {
|
||||
DiasporaFederation::Discovery::HCard.new(
|
||||
|
|
@ -32,6 +38,7 @@ module DiasporaFederation
|
|||
}
|
||||
let(:account) { alice.diaspora_id }
|
||||
let(:default_image) { "http://localhost:3000/assets/user/default.png" }
|
||||
subject { Discovery::Discovery.new(account) }
|
||||
|
||||
describe "#intialize" do
|
||||
it "sets diaspora* ID" do
|
||||
|
|
@ -47,15 +54,13 @@ module DiasporaFederation
|
|||
|
||||
describe ".fetch_and_save" do
|
||||
it "fetches the userdata and returns a person object" do
|
||||
stub_request(:get, "https://localhost:3000/.well-known/host-meta")
|
||||
.to_return(status: 200, body: host_meta_xrd)
|
||||
stub_request(:get, "http://localhost:3000/.well-known/webfinger.xml?resource=acct:#{account}")
|
||||
.to_return(status: 200, body: webfinger_xrd)
|
||||
stub_request(:get, "https://localhost:3000/.well-known/webfinger?resource=acct:#{account}")
|
||||
.to_return(status: 200, body: webfinger_jrd)
|
||||
stub_request(:get, "http://localhost:3000/hcard/users/#{alice.guid}")
|
||||
.to_return(status: 200, body: hcard_html)
|
||||
|
||||
expect_callback(:save_person_after_webfinger, kind_of(Entities::Person))
|
||||
person = Discovery::Discovery.new(account).fetch_and_save
|
||||
person = subject.fetch_and_save
|
||||
|
||||
expect(person.guid).to eq(alice.guid)
|
||||
expect(person.diaspora_id).to eq(account)
|
||||
|
|
@ -74,10 +79,8 @@ module DiasporaFederation
|
|||
end
|
||||
|
||||
it "fetches the userdata and saves the person object via callback" do
|
||||
stub_request(:get, "https://localhost:3000/.well-known/host-meta")
|
||||
.to_return(status: 200, body: host_meta_xrd)
|
||||
stub_request(:get, "http://localhost:3000/.well-known/webfinger.xml?resource=acct:#{account}")
|
||||
.to_return(status: 200, body: webfinger_xrd)
|
||||
stub_request(:get, "https://localhost:3000/.well-known/webfinger?resource=acct:#{account}")
|
||||
.to_return(status: 200, body: webfinger_jrd)
|
||||
stub_request(:get, "http://localhost:3000/hcard/users/#{alice.guid}")
|
||||
.to_return(status: 200, body: hcard_html)
|
||||
|
||||
|
|
@ -88,66 +91,196 @@ module DiasporaFederation
|
|||
callback_person = person
|
||||
end
|
||||
|
||||
expect(Discovery::Discovery.new(account).fetch_and_save).to be(callback_person)
|
||||
end
|
||||
|
||||
it "falls back to http if https fails with 404" do
|
||||
stub_request(:get, "https://localhost:3000/.well-known/host-meta")
|
||||
.to_return(status: 404)
|
||||
stub_request(:get, "http://localhost:3000/.well-known/host-meta")
|
||||
.to_return(status: 200, body: host_meta_xrd)
|
||||
stub_request(:get, "http://localhost:3000/.well-known/webfinger.xml?resource=acct:#{account}")
|
||||
.to_return(status: 200, body: webfinger_xrd)
|
||||
stub_request(:get, "http://localhost:3000/hcard/users/#{alice.guid}")
|
||||
.to_return(status: 200, body: hcard_html)
|
||||
|
||||
expect_callback(:save_person_after_webfinger, kind_of(Entities::Person))
|
||||
person = Discovery::Discovery.new(account).fetch_and_save
|
||||
|
||||
expect(person.guid).to eq(alice.guid)
|
||||
expect(person.diaspora_id).to eq(account)
|
||||
end
|
||||
|
||||
it "falls back to http if https fails with ssl error" do
|
||||
stub_request(:get, "https://localhost:3000/.well-known/host-meta")
|
||||
.to_raise(OpenSSL::SSL::SSLError)
|
||||
stub_request(:get, "http://localhost:3000/.well-known/host-meta")
|
||||
.to_return(status: 200, body: host_meta_xrd)
|
||||
stub_request(:get, "http://localhost:3000/.well-known/webfinger.xml?resource=acct:#{account}")
|
||||
.to_return(status: 200, body: webfinger_xrd)
|
||||
stub_request(:get, "http://localhost:3000/hcard/users/#{alice.guid}")
|
||||
.to_return(status: 200, body: hcard_html)
|
||||
|
||||
expect_callback(:save_person_after_webfinger, kind_of(Entities::Person))
|
||||
person = Discovery::Discovery.new(account).fetch_and_save
|
||||
|
||||
expect(person.guid).to eq(alice.guid)
|
||||
expect(person.diaspora_id).to eq(account)
|
||||
expect(subject.fetch_and_save).to be(callback_person)
|
||||
end
|
||||
|
||||
it "fails if the diaspora* ID does not match" do
|
||||
modified_webfinger = webfinger_xrd.gsub(account, "anonther_user@example.com")
|
||||
modified_webfinger = webfinger_jrd.gsub(account, "anonther_user@example.com")
|
||||
|
||||
stub_request(:get, "https://localhost:3000/.well-known/host-meta")
|
||||
.to_return(status: 200, body: host_meta_xrd)
|
||||
stub_request(:get, "http://localhost:3000/.well-known/webfinger.xml?resource=acct:#{account}")
|
||||
stub_request(:get, "https://localhost:3000/.well-known/webfinger?resource=acct:#{account}")
|
||||
.to_return(status: 200, body: modified_webfinger)
|
||||
|
||||
expect { Discovery::Discovery.new(account).fetch_and_save }.to raise_error Discovery::DiscoveryError
|
||||
expect { subject.fetch_and_save }.to raise_error Discovery::DiscoveryError
|
||||
end
|
||||
|
||||
it "fails if the diaspora* ID was not found" do
|
||||
stub_request(:get, "https://localhost:3000/.well-known/webfinger?resource=acct:#{account}")
|
||||
.to_return(status: 404)
|
||||
stub_request(:get, "http://localhost:3000/.well-known/webfinger?resource=acct:#{account}")
|
||||
.to_return(status: 404)
|
||||
stub_request(:get, "https://localhost:3000/.well-known/host-meta")
|
||||
.to_return(status: 200, body: host_meta_xrd)
|
||||
stub_request(:get, "http://localhost:3000/.well-known/webfinger.xml?resource=acct:#{account}")
|
||||
.to_return(status: 404)
|
||||
|
||||
expect { Discovery::Discovery.new(account).fetch_and_save }.to raise_error Discovery::DiscoveryError
|
||||
expect { subject.fetch_and_save }.to raise_error Discovery::DiscoveryError
|
||||
end
|
||||
|
||||
context "http fallback" do
|
||||
context "http fallback disabled (default)" do
|
||||
it "falls back to legacy WebFinger if https fails with 404" do
|
||||
stub_request(:get, "https://localhost:3000/.well-known/webfinger?resource=acct:#{account}")
|
||||
.to_return(status: 404)
|
||||
stub_request(:get, "https://localhost:3000/.well-known/host-meta")
|
||||
.to_return(status: 200, body: host_meta_xrd)
|
||||
stub_request(:get, "http://localhost:3000/.well-known/webfinger.xml?resource=acct:#{account}")
|
||||
.to_return(status: 200, body: webfinger_xrd)
|
||||
stub_request(:get, "http://localhost:3000/hcard/users/#{alice.guid}")
|
||||
.to_return(status: 200, body: hcard_html)
|
||||
|
||||
expect_callback(:save_person_after_webfinger, kind_of(Entities::Person))
|
||||
person = subject.fetch_and_save
|
||||
|
||||
expect(person.guid).to eq(alice.guid)
|
||||
expect(person.diaspora_id).to eq(account)
|
||||
end
|
||||
|
||||
it "falls back to legacy WebFinger if https fails with ssl error" do
|
||||
stub_request(:get, "https://localhost:3000/.well-known/webfinger?resource=acct:#{account}")
|
||||
.to_raise(OpenSSL::SSL::SSLError)
|
||||
stub_request(:get, "https://localhost:3000/.well-known/host-meta")
|
||||
.to_raise(OpenSSL::SSL::SSLError)
|
||||
stub_request(:get, "http://localhost:3000/.well-known/host-meta")
|
||||
.to_return(status: 200, body: host_meta_xrd)
|
||||
stub_request(:get, "http://localhost:3000/.well-known/webfinger.xml?resource=acct:#{account}")
|
||||
.to_return(status: 200, body: webfinger_xrd)
|
||||
stub_request(:get, "http://localhost:3000/hcard/users/#{alice.guid}")
|
||||
.to_return(status: 200, body: hcard_html)
|
||||
|
||||
expect_callback(:save_person_after_webfinger, kind_of(Entities::Person))
|
||||
person = subject.fetch_and_save
|
||||
|
||||
expect(person.guid).to eq(alice.guid)
|
||||
expect(person.diaspora_id).to eq(account)
|
||||
end
|
||||
end
|
||||
|
||||
context "http fallback enabled" do
|
||||
before :all do
|
||||
DiasporaFederation.webfinger_http_fallback = true
|
||||
end
|
||||
|
||||
after :all do
|
||||
DiasporaFederation.webfinger_http_fallback = false
|
||||
end
|
||||
|
||||
it "falls back to http if https fails with 404" do
|
||||
stub_request(:get, "https://localhost:3000/.well-known/webfinger?resource=acct:#{account}")
|
||||
.to_return(status: 404)
|
||||
stub_request(:get, "http://localhost:3000/.well-known/webfinger?resource=acct:#{account}")
|
||||
.to_return(status: 200, body: webfinger_jrd)
|
||||
stub_request(:get, "http://localhost:3000/hcard/users/#{alice.guid}")
|
||||
.to_return(status: 200, body: hcard_html)
|
||||
|
||||
expect_callback(:save_person_after_webfinger, kind_of(Entities::Person))
|
||||
person = subject.fetch_and_save
|
||||
|
||||
expect(person.guid).to eq(alice.guid)
|
||||
expect(person.diaspora_id).to eq(account)
|
||||
end
|
||||
|
||||
it "falls back to http if https fails with ssl error" do
|
||||
stub_request(:get, "https://localhost:3000/.well-known/webfinger?resource=acct:#{account}")
|
||||
.to_raise(OpenSSL::SSL::SSLError)
|
||||
stub_request(:get, "http://localhost:3000/.well-known/webfinger?resource=acct:#{account}")
|
||||
.to_return(status: 200, body: webfinger_jrd)
|
||||
stub_request(:get, "http://localhost:3000/hcard/users/#{alice.guid}")
|
||||
.to_return(status: 200, body: hcard_html)
|
||||
|
||||
expect_callback(:save_person_after_webfinger, kind_of(Entities::Person))
|
||||
person = subject.fetch_and_save
|
||||
|
||||
expect(person.guid).to eq(alice.guid)
|
||||
expect(person.diaspora_id).to eq(account)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "legacy WebFinger" do
|
||||
it "falls back to legacy WebFinger" do
|
||||
incomplete_webfinger_json = "{\"links\":[{\"rel\":\"http://openid.net/specs/connect/1.0/issuer\"," \
|
||||
"\"href\":\"https://localhost:3000/\"}],\"subject\":\"acct:#{account}\"}"
|
||||
stub_request(:get, "https://localhost:3000/.well-known/webfinger?resource=acct:#{account}")
|
||||
.to_return(status: 200, body: incomplete_webfinger_json)
|
||||
stub_request(:get, "https://localhost:3000/.well-known/host-meta")
|
||||
.to_return(status: 200, body: host_meta_xrd)
|
||||
stub_request(:get, "http://localhost:3000/.well-known/webfinger.xml?resource=acct:#{account}")
|
||||
.to_return(status: 200, body: webfinger_xrd)
|
||||
stub_request(:get, "http://localhost:3000/hcard/users/#{alice.guid}")
|
||||
.to_return(status: 200, body: hcard_html)
|
||||
|
||||
expect_callback(:save_person_after_webfinger, kind_of(Entities::Person))
|
||||
person = subject.fetch_and_save
|
||||
|
||||
expect(person.guid).to eq(alice.guid)
|
||||
expect(person.diaspora_id).to eq(account)
|
||||
expect(person.url).to eq(alice.url)
|
||||
expect(person.exported_key).to eq(alice.serialized_public_key)
|
||||
|
||||
profile = person.profile
|
||||
|
||||
expect(profile.diaspora_id).to eq(alice.diaspora_id)
|
||||
expect(profile.first_name).to eq("Dummy")
|
||||
expect(profile.last_name).to eq("User")
|
||||
|
||||
expect(profile.image_url).to eq(default_image)
|
||||
expect(profile.image_url_medium).to eq(default_image)
|
||||
expect(profile.image_url_small).to eq(default_image)
|
||||
end
|
||||
|
||||
it "falls back to http if https fails with 404" do
|
||||
stub_request(:get, "https://localhost:3000/.well-known/webfinger?resource=acct:#{account}")
|
||||
.to_return(status: 404)
|
||||
stub_request(:get, "https://localhost:3000/.well-known/host-meta")
|
||||
.to_return(status: 404)
|
||||
stub_request(:get, "http://localhost:3000/.well-known/host-meta")
|
||||
.to_return(status: 200, body: host_meta_xrd)
|
||||
stub_request(:get, "http://localhost:3000/.well-known/webfinger.xml?resource=acct:#{account}")
|
||||
.to_return(status: 200, body: webfinger_xrd)
|
||||
stub_request(:get, "http://localhost:3000/hcard/users/#{alice.guid}")
|
||||
.to_return(status: 200, body: hcard_html)
|
||||
|
||||
expect_callback(:save_person_after_webfinger, kind_of(Entities::Person))
|
||||
person = subject.fetch_and_save
|
||||
|
||||
expect(person.guid).to eq(alice.guid)
|
||||
expect(person.diaspora_id).to eq(account)
|
||||
end
|
||||
|
||||
it "falls back to http if https fails with ssl error" do
|
||||
stub_request(:get, "https://localhost:3000/.well-known/webfinger?resource=acct:#{account}")
|
||||
.to_raise(OpenSSL::SSL::SSLError)
|
||||
stub_request(:get, "https://localhost:3000/.well-known/host-meta")
|
||||
.to_raise(OpenSSL::SSL::SSLError)
|
||||
stub_request(:get, "http://localhost:3000/.well-known/host-meta")
|
||||
.to_return(status: 200, body: host_meta_xrd)
|
||||
stub_request(:get, "http://localhost:3000/.well-known/webfinger.xml?resource=acct:#{account}")
|
||||
.to_return(status: 200, body: webfinger_xrd)
|
||||
stub_request(:get, "http://localhost:3000/hcard/users/#{alice.guid}")
|
||||
.to_return(status: 200, body: hcard_html)
|
||||
|
||||
expect_callback(:save_person_after_webfinger, kind_of(Entities::Person))
|
||||
person = subject.fetch_and_save
|
||||
|
||||
expect(person.guid).to eq(alice.guid)
|
||||
expect(person.diaspora_id).to eq(account)
|
||||
end
|
||||
|
||||
it "fails if the diaspora* ID does not match" do
|
||||
modified_webfinger = webfinger_xrd.gsub(account, "anonther_user@example.com")
|
||||
|
||||
stub_request(:get, "https://localhost:3000/.well-known/webfinger?resource=acct:#{account}")
|
||||
.to_return(status: 200, body: "foobar")
|
||||
stub_request(:get, "https://localhost:3000/.well-known/host-meta")
|
||||
.to_return(status: 200, body: host_meta_xrd)
|
||||
stub_request(:get, "http://localhost:3000/.well-known/webfinger.xml?resource=acct:#{account}")
|
||||
.to_return(status: 200, body: modified_webfinger)
|
||||
|
||||
expect { subject.fetch_and_save }.to raise_error Discovery::DiscoveryError
|
||||
end
|
||||
end
|
||||
|
||||
context "error handling" do
|
||||
subject { Discovery::Discovery.new(account) }
|
||||
|
||||
it "re-raises DiscoveryError" do
|
||||
expect(subject).to receive(:validate_diaspora_id)
|
||||
.and_raise(Discovery::DiscoveryError, "Something went wrong!")
|
||||
|
|
|
|||
|
|
@ -39,6 +39,63 @@ XML
|
|||
</XRD>
|
||||
XML
|
||||
|
||||
let(:json) { <<-JSON }
|
||||
{
|
||||
"subject": "#{acct}",
|
||||
"aliases": [
|
||||
"#{person.alias_url}"
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"rel": "http://microformats.org/profile/hcard",
|
||||
"type": "text/html",
|
||||
"href": "#{person.hcard_url}"
|
||||
},
|
||||
{
|
||||
"rel": "http://joindiaspora.com/seed_location",
|
||||
"type": "text/html",
|
||||
"href": "#{person.url}"
|
||||
},
|
||||
{
|
||||
"rel": "http://webfinger.net/rel/profile-page",
|
||||
"type": "text/html",
|
||||
"href": "#{person.profile_url}"
|
||||
},
|
||||
{
|
||||
"rel": "http://schemas.google.com/g/2010#updates-from",
|
||||
"type": "application/atom+xml",
|
||||
"href": "#{person.atom_url}"
|
||||
},
|
||||
{
|
||||
"rel": "salmon",
|
||||
"href": "#{person.salmon_url}"
|
||||
},
|
||||
{
|
||||
"rel": "http://ostatus.org/schema/1.0/subscribe",
|
||||
"template": "http://somehost:3000/people?q={uri}"
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
|
||||
let(:minimal_json) { <<-JSON }
|
||||
{
|
||||
"subject": "#{acct}",
|
||||
"links": [
|
||||
{
|
||||
"rel": "http://microformats.org/profile/hcard",
|
||||
"type": "text/html",
|
||||
"href": "#{person.hcard_url}"
|
||||
},
|
||||
{
|
||||
"rel": "http://joindiaspora.com/seed_location",
|
||||
"type": "text/html",
|
||||
"href": "#{person.url}"
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
|
||||
let(:string) { "WebFinger:#{data[:acct_uri]}" }
|
||||
|
||||
it_behaves_like "an Entity subclass"
|
||||
|
|
@ -95,66 +152,11 @@ XML
|
|||
|
||||
context "json" do
|
||||
it "creates a nice JSON document" do
|
||||
json = <<-JSON
|
||||
{
|
||||
"subject": "#{acct}",
|
||||
"aliases": [
|
||||
"#{person.alias_url}"
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"rel": "http://microformats.org/profile/hcard",
|
||||
"type": "text/html",
|
||||
"href": "#{person.hcard_url}"
|
||||
},
|
||||
{
|
||||
"rel": "http://joindiaspora.com/seed_location",
|
||||
"type": "text/html",
|
||||
"href": "#{person.url}"
|
||||
},
|
||||
{
|
||||
"rel": "http://webfinger.net/rel/profile-page",
|
||||
"type": "text/html",
|
||||
"href": "#{person.profile_url}"
|
||||
},
|
||||
{
|
||||
"rel": "http://schemas.google.com/g/2010#updates-from",
|
||||
"type": "application/atom+xml",
|
||||
"href": "#{person.atom_url}"
|
||||
},
|
||||
{
|
||||
"rel": "salmon",
|
||||
"href": "#{person.salmon_url}"
|
||||
},
|
||||
{
|
||||
"rel": "http://ostatus.org/schema/1.0/subscribe",
|
||||
"template": "http://somehost:3000/people?q={uri}"
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
wf = Discovery::WebFinger.new(data, aliases: [person.alias_url])
|
||||
expect(JSON.pretty_generate(wf.to_json)).to eq(json.strip)
|
||||
end
|
||||
|
||||
it "creates minimal JSON document" do
|
||||
minimal_json = <<-JSON
|
||||
{
|
||||
"subject": "#{acct}",
|
||||
"links": [
|
||||
{
|
||||
"rel": "http://microformats.org/profile/hcard",
|
||||
"type": "text/html",
|
||||
"href": "#{person.hcard_url}"
|
||||
},
|
||||
{
|
||||
"rel": "http://joindiaspora.com/seed_location",
|
||||
"type": "text/html",
|
||||
"href": "#{person.url}"
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
wf = Discovery::WebFinger.new(minimal_data)
|
||||
expect(JSON.pretty_generate(wf.to_json)).to eq(minimal_json.strip)
|
||||
end
|
||||
|
|
@ -167,6 +169,9 @@ JSON
|
|||
"#{person.alias_url}",
|
||||
"#{person.profile_url}"
|
||||
],
|
||||
"properties": {
|
||||
"http://webfinger.example/ns/name": "Bob Smith"
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"rel": "http://microformats.org/profile/hcard",
|
||||
|
|
@ -191,10 +196,7 @@ JSON
|
|||
"rel": "http://openid.net/specs/connect/1.0/issuer",
|
||||
"href": "https://pod.example.tld/"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"http://webfinger.example/ns/name": "Bob Smith"
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
|
||||
|
|
@ -205,7 +207,7 @@ JSON
|
|||
end
|
||||
|
||||
context "parsing" do
|
||||
it "reads its own output" do
|
||||
it "reads its own xml output" do
|
||||
wf = Discovery::WebFinger.from_xml(xml)
|
||||
expect(wf.acct_uri).to eq(acct)
|
||||
expect(wf.hcard_url).to eq(person.hcard_url)
|
||||
|
|
@ -223,6 +225,24 @@ JSON
|
|||
expect(wf.seed_url).to eq(person.url)
|
||||
end
|
||||
|
||||
it "reads its own json output" do
|
||||
wf = Discovery::WebFinger.from_json(json)
|
||||
expect(wf.acct_uri).to eq(acct)
|
||||
expect(wf.hcard_url).to eq(person.hcard_url)
|
||||
expect(wf.seed_url).to eq(person.url)
|
||||
expect(wf.profile_url).to eq(person.profile_url)
|
||||
expect(wf.atom_url).to eq(person.atom_url)
|
||||
expect(wf.salmon_url).to eq(person.salmon_url)
|
||||
expect(wf.subscribe_url).to eq(person.subscribe_url)
|
||||
end
|
||||
|
||||
it "reads minimal json" do
|
||||
wf = Discovery::WebFinger.from_json(minimal_json)
|
||||
expect(wf.acct_uri).to eq(acct)
|
||||
expect(wf.hcard_url).to eq(person.hcard_url)
|
||||
expect(wf.seed_url).to eq(person.url)
|
||||
end
|
||||
|
||||
it "is frozen after parsing" do
|
||||
wf = Discovery::WebFinger.from_xml(xml)
|
||||
expect(wf).to be_frozen
|
||||
|
|
|
|||
|
|
@ -15,6 +15,36 @@ module DiasporaFederation
|
|||
</XRD>
|
||||
XML
|
||||
|
||||
let(:json) { <<-JSON }
|
||||
{
|
||||
"subject": "http://blog.example.com/article/id/314",
|
||||
"expires": "2010-01-30T09:30:00Z",
|
||||
"aliases": [
|
||||
"http://blog.example.com/cool_new_thing",
|
||||
"http://blog.example.com/steve/article/7"
|
||||
],
|
||||
"properties": {
|
||||
"http://blgx.example.net/ns/version": "1.3",
|
||||
"http://blgx.example.net/ns/ext": null
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"rel": "author",
|
||||
"type": "text/html",
|
||||
"href": "http://blog.example.com/author/steve"
|
||||
},
|
||||
{
|
||||
"rel": "author",
|
||||
"href": "http://example.com/author/john"
|
||||
},
|
||||
{
|
||||
"rel": "copyright",
|
||||
"template": "http://example.com/copyright?id={uri}"
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
|
||||
let(:data) {
|
||||
{
|
||||
subject: "http://blog.example.com/article/id/314",
|
||||
|
|
@ -72,7 +102,7 @@ XML
|
|||
|
||||
describe "#to_json" do
|
||||
it "provides the hash for json" do
|
||||
expect(doc.to_json).to eq(data)
|
||||
expect(JSON.pretty_generate(doc.to_json)).to eq(json.strip)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -90,5 +120,16 @@ XML
|
|||
expect { Discovery::XrdDocument.xml_data("<html></html>") }.to raise_error Discovery::InvalidDocument
|
||||
end
|
||||
end
|
||||
|
||||
describe ".json_data" do
|
||||
it "reads the json document" do
|
||||
hash = Discovery::XrdDocument.json_data(json)
|
||||
expect(hash).to eq(data)
|
||||
end
|
||||
|
||||
it "raises InvalidDocument when a JSON error occurs" do
|
||||
expect { Discovery::XrdDocument.json_data("foo") }.to raise_error Discovery::InvalidDocument
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue