diff --git a/Changelog.md b/Changelog.md
index 200697b37..787774e78 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -65,6 +65,7 @@ We use yarn to install the frontend dependencies now, so you need to have that i
## Features
* Render posts and comments as HTML in HTML mails [#8365](https://github.com/diaspora/diaspora/pull/8365)
+* Add NodeInfo 2.1 support and also read newer versions of NodeInfo [#8379](https://github.com/diaspora/diaspora/pull/8379)
# 0.7.17.0
diff --git a/app/assets/templates/pod_table_entry_tpl.jst.hbs b/app/assets/templates/pod_table_entry_tpl.jst.hbs
index 46f0a9909..3c2bb0605 100644
--- a/app/assets/templates/pod_table_entry_tpl.jst.hbs
+++ b/app/assets/templates/pod_table_entry_tpl.jst.hbs
@@ -1,5 +1,5 @@
-
+ |
{{#if ssl}}
{{else}}
@@ -14,6 +14,7 @@
|
{{#if has_no_errors}}
+ {{software}}
{{else}}
{{status_text}}
{{/if}}
diff --git a/app/presenters/node_info_presenter.rb b/app/presenters/node_info_presenter.rb
index ee7aec9b3..3c283e87c 100644
--- a/app/presenters/node_info_presenter.rb
+++ b/app/presenters/node_info_presenter.rb
@@ -29,6 +29,8 @@ class NodeInfoPresenter
def add_static_data(doc)
doc.software.name = "diaspora"
+ doc.software.repository = "https://github.com/diaspora/diaspora"
+ doc.software.homepage = "https://diasporafoundation.org/"
doc.protocols.protocols << "diaspora"
end
diff --git a/lib/connection_tester.rb b/lib/connection_tester.rb
index 39dc6959b..4fd210b93 100644
--- a/lib/connection_tester.rb
+++ b/lib/connection_tester.rb
@@ -3,7 +3,6 @@
class ConnectionTester
include Diaspora::Logging
- NODEINFO_SCHEMA = "http://nodeinfo.diaspora.software/ns/schema/1.0"
NODEINFO_FRAGMENT = "/.well-known/nodeinfo"
class << self
@@ -124,8 +123,11 @@ class ConnectionTester
def nodeinfo
with_http_connection do |http|
ni_resp = http.get(NODEINFO_FRAGMENT)
- nd_resp = http.get(find_nodeinfo_url(ni_resp.body))
- find_software_version(nd_resp.body)
+ ni_urls = find_nodeinfo_urls(ni_resp.body)
+ raise NodeInfoFailure, "No supported NodeInfo version found" if ni_urls.empty?
+
+ version, url = ni_urls.max
+ find_software_version(version, http.get(url).body)
end
rescue Faraday::ClientError => e
raise HTTPFailure, "#{e.class}: #{e.message}"
@@ -180,20 +182,23 @@ class ConnectionTester
@result.ssl = (response.env.url.scheme == "https")
end
- # walk the JSON document, get the actual document location
- def find_nodeinfo_url(body)
+ # walk the JSON document, get the actual document locations
+ def find_nodeinfo_urls(body)
jrd = JSON.parse(body)
links = jrd.fetch("links")
raise NodeInfoFailure, "invalid JRD: '#/links' is not an array!" unless links.is_a?(Array)
- links.find { |entry|
- entry.fetch("rel") == NODEINFO_SCHEMA
- }.fetch("href")
+
+ supported_rel_map = NodeInfo::VERSIONS.index_by {|v| "http://nodeinfo.diaspora.software/ns/schema/#{v}" }
+ links.map {|entry|
+ version = supported_rel_map[entry.fetch("rel")]
+ [version, entry.fetch("href")] if version
+ }.compact.to_h
end
# walk the JSON document, find the version string
- def find_software_version(body)
+ def find_software_version(version, body)
info = JSON.parse(body)
- JSON::Validator.validate!(NodeInfo.schema("1.0"), info)
+ JSON::Validator.validate!(NodeInfo.schema(version), info)
sw = info.fetch("software")
@result.software_version = "#{sw.fetch('name')} #{sw.fetch('version')}"
end
diff --git a/lib/node_info.rb b/lib/node_info.rb
index f2118af26..05c96dcb1 100644
--- a/lib/node_info.rb
+++ b/lib/node_info.rb
@@ -4,13 +4,13 @@ require "pathname"
require "json-schema"
module NodeInfo
- VERSIONS = %w(1.0 2.0).freeze
- SCHEMAS = {}
- private_constant :VERSIONS, :SCHEMAS
+ VERSIONS = %w[1.0 2.0 2.1].freeze
+ SCHEMAS = {} # rubocop:disable Style/MutableConstant
+ private_constant :SCHEMAS
- # rubocop:disable Metrics/BlockLength
Document = Struct.new(:version, :software, :protocols, :services, :open_registrations, :usage, :metadata) do
- Software = Struct.new(:name, :version) do
+ # rubocop:disable Lint/ConstantDefinitionInBlock
+ Software = Struct.new(:name, :version, :repository, :homepage) do
def initialize(name=nil, version=nil)
super(name, version)
end
@@ -21,6 +21,13 @@ module NodeInfo
"version" => version
}
end
+
+ def version_21_hash
+ version_10_hash.merge(
+ "repository" => repository,
+ "homepage" => homepage
+ )
+ end
end
Protocols = Struct.new(:protocols) do
@@ -80,6 +87,7 @@ module NodeInfo
}
end
end
+ # rubocop:enable Lint/ConstantDefinitionInBlock
def self.build
new.tap do |doc|
@@ -98,6 +106,8 @@ module NodeInfo
version_10_hash
when "2.0"
version_20_hash
+ when "2.1"
+ version_21_hash
end
end
@@ -144,6 +154,18 @@ module NodeInfo
)
end
+ def version_21_hash
+ deep_compact(
+ "version" => "2.1",
+ "software" => software.version_21_hash,
+ "protocols" => protocols.version_20_array,
+ "services" => services.version_10_hash,
+ "openRegistrations" => open_registrations,
+ "usage" => usage.version_10_hash,
+ "metadata" => metadata
+ )
+ end
+
def deep_compact(hash)
hash.tap do |hash|
hash.reject! {|_, value|
@@ -153,7 +175,6 @@ module NodeInfo
end
end
end
- # rubocop:enable Metrics/BlockLength
def self.schema(version)
SCHEMAS[version] ||= JSON.parse(
diff --git a/spec/controllers/node_info_controller_spec.rb b/spec/controllers/node_info_controller_spec.rb
index fcad4bc31..5c2925cbb 100644
--- a/spec/controllers/node_info_controller_spec.rb
+++ b/spec/controllers/node_info_controller_spec.rb
@@ -20,6 +20,9 @@ describe NodeInfoController do
}, {
"rel" => "http://nodeinfo.diaspora.software/ns/schema/2.0",
"href" => node_info_url("2.0")
+ }, {
+ "rel" => "http://nodeinfo.diaspora.software/ns/schema/2.1",
+ "href" => node_info_url("2.1")
}]
end
end
@@ -33,7 +36,7 @@ describe NodeInfoController do
end
end
- %w(1.0 2.0).each do |version|
+ %w[1.0 2.0 2.1].each do |version|
context "version #{version}" do
it "responds to JSON" do
get :document, params: {version: version}, format: :json
diff --git a/spec/lib/connection_tester_spec.rb b/spec/lib/connection_tester_spec.rb
index 7a11cc0a3..671fec860 100644
--- a/spec/lib/connection_tester_spec.rb
+++ b/spec/lib/connection_tester_spec.rb
@@ -106,25 +106,61 @@ describe ConnectionTester do
end
describe "#nodeinfo" do
- let(:ni_wellknown) { {links: [{rel: ConnectionTester::NODEINFO_SCHEMA, href: "/nodeinfo"}]} }
-
- it "reads the version from the nodeinfo document" do
- ni_document = NodeInfo.build do |doc|
- doc.version = "1.0"
+ def build_ni_document(version)
+ NodeInfo.build do |doc|
+ doc.version = version
doc.open_registrations = true
doc.protocols.protocols << "diaspora"
doc.software.name = "diaspora"
doc.software.version = "a.b.c.d"
end
+ end
+
+ NodeInfo::VERSIONS.each do |version|
+ context "with version #{version}" do
+ let(:ni_wellknown) {
+ {links: [{rel: "http://nodeinfo.diaspora.software/ns/schema/#{version}", href: "/nodeinfo/#{version}"}]}
+ }
+
+ it "reads the version from the nodeinfo document" do
+ ni_document = build_ni_document(version)
+
+ stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}")
+ .to_return(status: 200, body: JSON.generate(ni_wellknown))
+ stub_request(:get, "#{url}/nodeinfo/#{version}")
+ .to_return(status: 200, body: JSON.generate(ni_document.as_json))
+
+ tester.nodeinfo
+ expect(result.software_version).to eq("diaspora a.b.c.d")
+ end
+ end
+ end
+
+ it "uses the latest commonly supported version" do
+ ni_wellknown = {links: [
+ {rel: "http://nodeinfo.diaspora.software/ns/schema/1.0", href: "/nodeinfo/1.0"},
+ {rel: "http://nodeinfo.diaspora.software/ns/schema/1.1", href: "/nodeinfo/1.1"},
+ {rel: "http://nodeinfo.diaspora.software/ns/schema/2.0", href: "/nodeinfo/2.0"},
+ {rel: "http://nodeinfo.diaspora.software/ns/schema/9.0", href: "/nodeinfo/9.0"}
+ ]}
+
+ ni_document = build_ni_document("2.0")
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}")
.to_return(status: 200, body: JSON.generate(ni_wellknown))
- stub_request(:get, "#{url}/nodeinfo").to_return(status: 200, body: JSON.generate(ni_document.as_json))
+ stub_request(:get, "#{url}/nodeinfo/2.0").to_return(status: 200, body: JSON.generate(ni_document.as_json))
tester.nodeinfo
expect(result.software_version).to eq("diaspora a.b.c.d")
end
+ it "handles no common version gracefully" do
+ ni_wellknown = {links: [{rel: "http://nodeinfo.diaspora.software/ns/schema/1.1", href: "/nodeinfo/1.1"}]}
+ stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}")
+ .to_return(status: 200, body: JSON.generate(ni_wellknown))
+ expect { tester.nodeinfo }.to raise_error(ConnectionTester::NodeInfoFailure)
+ end
+
it "fails the nodeinfo document is missing" do
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}").to_return(status: 404, body: "Not Found")
expect { tester.nodeinfo }.to raise_error(ConnectionTester::HTTPFailure)
@@ -137,16 +173,17 @@ describe ConnectionTester do
end
it "handles a invalid jrd document gracefully" do
- invalid_wellknown = {links: {rel: ConnectionTester::NODEINFO_SCHEMA, href: "/nodeinfo"}}
+ invalid_wellknown = {links: {rel: "http://nodeinfo.diaspora.software/ns/schema/1.0", href: "/nodeinfo/1.0"}}
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}")
.to_return(status: 200, body: JSON.generate(invalid_wellknown))
expect { tester.nodeinfo }.to raise_error(ConnectionTester::NodeInfoFailure)
end
it "handles a invalid nodeinfo document gracefully" do
+ ni_wellknown = {links: [{rel: "http://nodeinfo.diaspora.software/ns/schema/1.0", href: "/nodeinfo/1.0"}]}
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}")
.to_return(status: 200, body: JSON.generate(ni_wellknown))
- stub_request(:get, "#{url}/nodeinfo").to_return(status: 200, body: '{"software": "invalid nodeinfo"}')
+ stub_request(:get, "#{url}/nodeinfo/1.0").to_return(status: 200, body: '{"software": "invalid nodeinfo"}')
expect { tester.nodeinfo }.to raise_error(ConnectionTester::NodeInfoFailure)
end
end
diff --git a/spec/presenters/node_info_presenter_spec.rb b/spec/presenters/node_info_presenter_spec.rb
index ab4cb09c2..4ae6bfa86 100644
--- a/spec/presenters/node_info_presenter_spec.rb
+++ b/spec/presenters/node_info_presenter_spec.rb
@@ -171,5 +171,36 @@ describe NodeInfoPresenter do
)
end
end
+
+ context "version 2.1" do
+ it "provides generic pod data in json" do
+ expect(NodeInfoPresenter.new("2.1").as_json.as_json).to eq(
+ "version" => "2.1",
+ "software" => {
+ "name" => "diaspora",
+ "version" => AppConfig.version_string,
+ "repository" => "https://github.com/diaspora/diaspora",
+ "homepage" => "https://diasporafoundation.org/"
+ },
+ "protocols" => ["diaspora"],
+ "services" => {
+ "inbound" => [],
+ "outbound" => AppConfig.configured_services.map(&:to_s)
+ },
+ "openRegistrations" => AppConfig.settings.enable_registrations?,
+ "usage" => {
+ "users" => {}
+ },
+ "metadata" => {
+ "nodeName" => AppConfig.settings.pod_name,
+ "camo" => {
+ "markdown" => AppConfig.privacy.camo.proxy_markdown_images?,
+ "opengraph" => AppConfig.privacy.camo.proxy_opengraph_thumbnails?,
+ "remotePods" => AppConfig.privacy.camo.proxy_remote_pod_images?
+ }
+ }
+ )
+ end
+ end
end
end
diff --git a/vendor/nodeinfo/schemas/2.0.json b/vendor/nodeinfo/schemas/2.0.json
index ddaca625e..da56bed13 100644
--- a/vendor/nodeinfo/schemas/2.0.json
+++ b/vendor/nodeinfo/schemas/2.0.json
@@ -1,5 +1,3 @@
-
-
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "http://nodeinfo.diaspora.software/ns/schema/2.0#",
diff --git a/vendor/nodeinfo/schemas/2.1.json b/vendor/nodeinfo/schemas/2.1.json
new file mode 100644
index 000000000..561e64479
--- /dev/null
+++ b/vendor/nodeinfo/schemas/2.1.json
@@ -0,0 +1,188 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "id": "http://nodeinfo.diaspora.software/ns/schema/2.1#",
+ "description": "NodeInfo schema version 2.1.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "version",
+ "software",
+ "protocols",
+ "services",
+ "openRegistrations",
+ "usage",
+ "metadata"
+ ],
+ "properties": {
+ "version": {
+ "description": "The schema version, must be 2.1.",
+ "enum": [
+ "2.1"
+ ]
+ },
+ "software": {
+ "description": "Metadata about server software in use.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "name",
+ "version"
+ ],
+ "properties": {
+ "name": {
+ "description": "The canonical name of this server software.",
+ "type": "string",
+ "pattern": "^[a-z0-9-]+$"
+ },
+ "version": {
+ "description": "The version of this server software.",
+ "type": "string"
+ },
+ "repository": {
+ "description": "The url of the source code repository of this server software.",
+ "type": "string"
+ },
+ "homepage": {
+ "description": "The url of the homepage of this server software.",
+ "type": "string"
+ }
+ }
+ },
+ "protocols": {
+ "description": "The protocols supported on this server.",
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "enum": [
+ "activitypub",
+ "buddycloud",
+ "dfrn",
+ "diaspora",
+ "libertree",
+ "ostatus",
+ "pumpio",
+ "tent",
+ "xmpp",
+ "zot"
+ ]
+ }
+ },
+ "services": {
+ "description": "The third party sites this server can connect to via their application API.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "inbound",
+ "outbound"
+ ],
+ "properties": {
+ "inbound": {
+ "description": "The third party sites this server can retrieve messages from for combined display with regular traffic.",
+ "type": "array",
+ "minItems": 0,
+ "items": {
+ "enum": [
+ "atom1.0",
+ "gnusocial",
+ "imap",
+ "pnut",
+ "pop3",
+ "pumpio",
+ "rss2.0",
+ "twitter"
+ ]
+ }
+ },
+ "outbound": {
+ "description": "The third party sites this server can publish messages to on the behalf of a user.",
+ "type": "array",
+ "minItems": 0,
+ "items": {
+ "enum": [
+ "atom1.0",
+ "blogger",
+ "buddycloud",
+ "diaspora",
+ "dreamwidth",
+ "drupal",
+ "facebook",
+ "friendica",
+ "gnusocial",
+ "google",
+ "insanejournal",
+ "libertree",
+ "linkedin",
+ "livejournal",
+ "mediagoblin",
+ "myspace",
+ "pinterest",
+ "pnut",
+ "posterous",
+ "pumpio",
+ "redmatrix",
+ "rss2.0",
+ "smtp",
+ "tent",
+ "tumblr",
+ "twitter",
+ "wordpress",
+ "xmpp"
+ ]
+ }
+ }
+ }
+ },
+ "openRegistrations": {
+ "description": "Whether this server allows open self-registration.",
+ "type": "boolean"
+ },
+ "usage": {
+ "description": "Usage statistics for this server.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "users"
+ ],
+ "properties": {
+ "users": {
+ "description": "statistics about the users of this server.",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "total": {
+ "description": "The total amount of on this server registered users.",
+ "type": "integer",
+ "minimum": 0
+ },
+ "activeHalfyear": {
+ "description": "The amount of users that signed in at least once in the last 180 days.",
+ "type": "integer",
+ "minimum": 0
+ },
+ "activeMonth": {
+ "description": "The amount of users that signed in at least once in the last 30 days.",
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ },
+ "localPosts": {
+ "description": "The amount of posts that were made by users that are registered on this server.",
+ "type": "integer",
+ "minimum": 0
+ },
+ "localComments": {
+ "description": "The amount of comments that were made by users that are registered on this server.",
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ },
+ "metadata": {
+ "description": "Free form key value pairs for software specific values. Clients should not rely on any specific key present.",
+ "type": "object",
+ "minProperties": 0,
+ "additionalProperties": true
+ }
+ }
+}
|