From 6cdbc345f4ed824b9491ac6b53406c44e23f7ba5 Mon Sep 17 00:00:00 2001
From: Meisam <39205857+MFTabriz@users.noreply.github.com>
Date: Thu, 15 Dec 2022 15:43:05 +0100
Subject: [PATCH] Validate nodeinfo response by schema (#21395)
* add json-schema to :test in Gemfile
* Create node_info_2.0_schema.json
* test match_response_schema
* Create match_response_schema.rb
* Update nodeinfo_controller_spec.rb
* Rename spec/support/node_info_2.0_schema.json to spec/support/schema/node_info_2.0_schema.json
* Update match_response_schema.rb
* cleanup
* additionally validate the json schema itself
disable throwing errors
test the schema matcher
* rename nodeinfo schema to nodeinfo_2.0
* use Rails.root.join to construct the path
* prettify json
* sync Gemfile.lock
---
Gemfile | 5 +-
Gemfile.lock | 3 +
.../well_known/nodeinfo_controller_spec.rb | 2 +
.../matchers/json/match_json_schema.rb | 6 +
spec/support/schema/nodeinfo_2.0.json | 170 ++++++++++++++++++
5 files changed, 184 insertions(+), 2 deletions(-)
create mode 100644 spec/support/matchers/json/match_json_schema.rb
create mode 100644 spec/support/schema/nodeinfo_2.0.json
diff --git a/Gemfile b/Gemfile
index a399f51023..9fe2a11209 100644
--- a/Gemfile
+++ b/Gemfile
@@ -117,13 +117,14 @@ group :test do
gem 'capybara', '~> 3.38'
gem 'climate_control', '~> 0.2'
gem 'faker', '~> 3.0'
+ gem 'json-schema', '~> 3.0'
gem 'microformats', '~> 4.4'
+ gem 'rack-test', '~> 2.0'
gem 'rails-controller-testing', '~> 1.0'
+ gem 'rspec_junit_formatter', '~> 0.6'
gem 'rspec-sidekiq', '~> 3.1'
gem 'simplecov', '~> 0.21', require: false
gem 'webmock', '~> 3.18'
- gem 'rspec_junit_formatter', '~> 0.6'
- gem 'rack-test', '~> 2.0'
end
group :development do
diff --git a/Gemfile.lock b/Gemfile.lock
index 578a88436b..ac3f560ea1 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -344,6 +344,8 @@ GEM
json-ld-preloaded (3.2.2)
json-ld (~> 3.2)
rdf (~> 3.2)
+ json-schema (3.0.0)
+ addressable (>= 2.8)
jsonapi-renderer (0.2.2)
jwt (2.4.1)
kaminari (1.2.2)
@@ -791,6 +793,7 @@ DEPENDENCIES
idn-ruby
json-ld
json-ld-preloaded (~> 3.2)
+ json-schema (~> 3.0)
kaminari (~> 1.2)
kt-paperclip (~> 7.1)
letter_opener (~> 1.8)
diff --git a/spec/controllers/well_known/nodeinfo_controller_spec.rb b/spec/controllers/well_known/nodeinfo_controller_spec.rb
index 694bb0fb9f..36e85f20d1 100644
--- a/spec/controllers/well_known/nodeinfo_controller_spec.rb
+++ b/spec/controllers/well_known/nodeinfo_controller_spec.rb
@@ -27,6 +27,8 @@ describe WellKnown::NodeInfoController, type: :controller do
json = body_as_json
+ expect({ "foo" => 0 }).not_to match_json_schema("nodeinfo_2.0")
+ expect(json).to match_json_schema("nodeinfo_2.0")
expect(json[:version]).to eq '2.0'
expect(json[:usage]).to be_a Hash
expect(json[:software]).to be_a Hash
diff --git a/spec/support/matchers/json/match_json_schema.rb b/spec/support/matchers/json/match_json_schema.rb
new file mode 100644
index 0000000000..5d9c9a618e
--- /dev/null
+++ b/spec/support/matchers/json/match_json_schema.rb
@@ -0,0 +1,6 @@
+RSpec::Matchers.define :match_json_schema do |schema|
+ match do |input_json|
+ schema_path = Rails.root.join('spec', 'support', 'schema', "#{schema}.json").to_s
+ JSON::Validator.validate(schema_path, input_json, validate_schema: true)
+ end
+end
diff --git a/spec/support/schema/nodeinfo_2.0.json b/spec/support/schema/nodeinfo_2.0.json
new file mode 100644
index 0000000000..085ce542bd
--- /dev/null
+++ b/spec/support/schema/nodeinfo_2.0.json
@@ -0,0 +1,170 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "id": "http://nodeinfo.diaspora.software/ns/schema/2.0#",
+ "description": "NodeInfo schema version 2.0.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "version",
+ "software",
+ "protocols",
+ "services",
+ "openRegistrations",
+ "usage",
+ "metadata"
+ ],
+ "properties": {
+ "version": {
+ "description": "The schema version, must be 2.0.",
+ "enum": ["2.0"]
+ },
+ "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"
+ }
+ }
+ },
+ "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
+ }
+ }
+}
--
GitLab