From 46d5dda916830aa6ce77c364148fbb7c041bccd8 Mon Sep 17 00:00:00 2001 From: Pia B Date: Fri, 7 Aug 2026 16:46:28 +0200 Subject: [PATCH 1/6] add addressable and spec --- fasp_data_sharing/Gemfile | 2 ++ fasp_data_sharing/Gemfile.lock | 1 + .../fasp_data_sharing/activity_pub_object.rb | 2 +- .../activity_pub_object_test.rb | 20 +++++++++++++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/fasp_data_sharing/Gemfile b/fasp_data_sharing/Gemfile index 5ea621b..519724e 100644 --- a/fasp_data_sharing/Gemfile +++ b/fasp_data_sharing/Gemfile @@ -9,6 +9,8 @@ gem "sqlite3" gem "propshaft" +gem "addressable" + # Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/] gem "rubocop-rails-omakase", require: false diff --git a/fasp_data_sharing/Gemfile.lock b/fasp_data_sharing/Gemfile.lock index 9169835..003bafe 100644 --- a/fasp_data_sharing/Gemfile.lock +++ b/fasp_data_sharing/Gemfile.lock @@ -332,6 +332,7 @@ PLATFORMS x86_64-linux-musl DEPENDENCIES + addressable debug (>= 1.0.0) fasp_base! fasp_data_sharing! diff --git a/fasp_data_sharing/app/models/fasp_data_sharing/activity_pub_object.rb b/fasp_data_sharing/app/models/fasp_data_sharing/activity_pub_object.rb index bda772d..0e93b59 100644 --- a/fasp_data_sharing/app/models/fasp_data_sharing/activity_pub_object.rb +++ b/fasp_data_sharing/app/models/fasp_data_sharing/activity_pub_object.rb @@ -52,7 +52,7 @@ def signature_headers end def sign(headers) - parsed_uri = URI(uri) + parsed_uri = Addressable::URI.parse(uri).normalize target = parsed_uri.path target << "?#{parsed_uri.query}" unless parsed_uri.query.nil? string = [ diff --git a/fasp_data_sharing/test/models/fasp_data_sharing/activity_pub_object_test.rb b/fasp_data_sharing/test/models/fasp_data_sharing/activity_pub_object_test.rb index 47f6fef..9021af4 100644 --- a/fasp_data_sharing/test/models/fasp_data_sharing/activity_pub_object_test.rb +++ b/fasp_data_sharing/test/models/fasp_data_sharing/activity_pub_object_test.rb @@ -42,5 +42,25 @@ class ActivityPubObjectTest < ActiveSupport::TestCase assert_requested(message_signatures_stub) assert_requested(signatures_stub) end + + test "when URI contains non-ASCII characters it gets normalized" do + uri = "https://other.example.com/users/\u4F11\u65E5\u8AB2\u9577" + json_object = { + "@context" => "https://www.w3.org/ns/activitystreams", + "id" => uri + } + activity_pub_object = ActivityPubObject.new(uri: uri) + message_signatures_stub = stub_request(:get, @uri) + .with { |r| r.headers["Signature-Input"].present? } + .to_return_json( + body: json_object, + headers: { "content-type" => "application/activity+json" + }) + + returned_json = activity_pub_object.fetch + + assert_equal json_object, returned_json + assert_requested(message_signatures_stub) + end end end From 9463af99ef5008274b3fc6143ad78ce99dd6e830 Mon Sep 17 00:00:00 2001 From: Pia B Date: Fri, 7 Aug 2026 16:51:42 +0200 Subject: [PATCH 2/6] add idnx gem --- fasp_data_sharing/Gemfile | 1 + fasp_data_sharing/Gemfile.lock | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/fasp_data_sharing/Gemfile b/fasp_data_sharing/Gemfile index 519724e..1521e64 100644 --- a/fasp_data_sharing/Gemfile +++ b/fasp_data_sharing/Gemfile @@ -10,6 +10,7 @@ gem "sqlite3" gem "propshaft" gem "addressable" +gem "idnx" # Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/] gem "rubocop-rails-omakase", require: false diff --git a/fasp_data_sharing/Gemfile.lock b/fasp_data_sharing/Gemfile.lock index 003bafe..8c5f453 100644 --- a/fasp_data_sharing/Gemfile.lock +++ b/fasp_data_sharing/Gemfile.lock @@ -113,6 +113,16 @@ GEM drb (2.2.3) erb (5.1.3) erubi (1.13.1) + ffi (1.17.4-aarch64-linux-gnu) + ffi (1.17.4-aarch64-linux-musl) + ffi (1.17.4-arm-linux-gnu) + ffi (1.17.4-arm-linux-musl) + ffi (1.17.4-arm64-darwin) + ffi (1.17.4-x86-linux-gnu) + ffi (1.17.4-x86-linux-musl) + ffi (1.17.4-x86_64-darwin) + ffi (1.17.4-x86_64-linux-gnu) + ffi (1.17.4-x86_64-linux-musl) forwardable (1.3.3) globalid (1.3.0) activesupport (>= 6.1) @@ -122,6 +132,8 @@ GEM http-2 (>= 1.0.0) i18n (1.14.7) concurrent-ruby (~> 1.0) + idnx (0.1.1) + ffi (~> 1.12) io-console (0.8.1) irb (1.15.3) pp (>= 0.6.0) @@ -336,6 +348,7 @@ DEPENDENCIES debug (>= 1.0.0) fasp_base! fasp_data_sharing! + idnx propshaft puma rubocop-rails-omakase From 826cb2fa51b05f3dba7e41e92910c00e574e5f82 Mon Sep 17 00:00:00 2001 From: Pia B Date: Fri, 7 Aug 2026 17:05:29 +0200 Subject: [PATCH 3/6] fix stub --- .../fasp_data_sharing/activity_pub_object_test.rb | 2 +- fasp_data_sharing/test/test_helper.rb | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/fasp_data_sharing/test/models/fasp_data_sharing/activity_pub_object_test.rb b/fasp_data_sharing/test/models/fasp_data_sharing/activity_pub_object_test.rb index 9021af4..91ffdc2 100644 --- a/fasp_data_sharing/test/models/fasp_data_sharing/activity_pub_object_test.rb +++ b/fasp_data_sharing/test/models/fasp_data_sharing/activity_pub_object_test.rb @@ -50,7 +50,7 @@ class ActivityPubObjectTest < ActiveSupport::TestCase "id" => uri } activity_pub_object = ActivityPubObject.new(uri: uri) - message_signatures_stub = stub_request(:get, @uri) + message_signatures_stub = stub_request(:get, uri) .with { |r| r.headers["Signature-Input"].present? } .to_return_json( body: json_object, diff --git a/fasp_data_sharing/test/test_helper.rb b/fasp_data_sharing/test/test_helper.rb index 098c60e..85817fe 100644 --- a/fasp_data_sharing/test/test_helper.rb +++ b/fasp_data_sharing/test/test_helper.rb @@ -1,6 +1,13 @@ # Configure Rails Environment ENV["RAILS_ENV"] = "test" +require "webmock" +require "httpx/adapters/webmock" +require "webmock/minitest" +WebMock.disable_net_connect!( + allow_localhost: true +) + require_relative "../test/dummy/config/environment" ActiveRecord::Migrator.migrations_paths = [ File.expand_path("../test/dummy/db/migrate", __dir__) ] ActiveRecord::Migrator.migrations_paths << File.expand_path("../db/migrate", __dir__) @@ -14,11 +21,5 @@ ActiveSupport::TestCase.fixtures :all end -require "webmock" -require "httpx/adapters/webmock" -require "webmock/minitest" -WebMock.disable_net_connect!( - allow_localhost: true -) Dir[File.expand_path("support/**/*.rb", __dir__)].each { |f| require f } From ed16babe864ff6b67be47d2656c096b5b53d41b4 Mon Sep 17 00:00:00 2001 From: Pia B Date: Mon, 10 Aug 2026 14:55:57 +0200 Subject: [PATCH 4/6] move gems to .gemspec --- fasp_base/Gemfile.lock | 42 +++++++++---------- fasp_base/fasp_base.gemspec | 2 + fasp_data_sharing/Gemfile | 3 -- fasp_data_sharing/Gemfile.lock | 36 +++------------- .../fasp_data_sharing/activity_pub_object.rb | 2 +- .../activity_pub_object_test.rb | 2 +- 6 files changed, 30 insertions(+), 57 deletions(-) diff --git a/fasp_base/Gemfile.lock b/fasp_base/Gemfile.lock index 36f8f47..3d0eb0f 100644 --- a/fasp_base/Gemfile.lock +++ b/fasp_base/Gemfile.lock @@ -2,8 +2,10 @@ PATH remote: . specs: fasp_base (0.1.0) + addressable bcrypt httpx + idnx linzer (>= 0.7.7) openssl rails (>= 8.0.0) @@ -106,6 +108,16 @@ GEM drb (2.2.3) erb (6.0.1) erubi (1.13.1) + ffi (1.17.4-aarch64-linux-gnu) + ffi (1.17.4-aarch64-linux-musl) + ffi (1.17.4-arm-linux-gnu) + ffi (1.17.4-arm-linux-musl) + ffi (1.17.4-arm64-darwin) + ffi (1.17.4-x86-linux-gnu) + ffi (1.17.4-x86-linux-musl) + ffi (1.17.4-x86_64-darwin) + ffi (1.17.4-x86_64-linux-gnu) + ffi (1.17.4-x86_64-linux-musl) forwardable (1.4.0) globalid (1.3.0) activesupport (>= 6.1) @@ -115,6 +127,8 @@ GEM http-2 (>= 1.2.0) i18n (1.14.7) concurrent-ruby (~> 1.0) + idnx (0.1.1) + ffi (~> 1.12) io-console (0.8.2) irb (1.16.0) pp (>= 0.6.0) @@ -144,7 +158,7 @@ GEM marcel (1.1.0) mini_mime (1.1.5) mini_portile2 (2.8.9) - minitest (5.26.0) + minitest (5.27.0) net-http (0.9.1) uri (>= 0.11.1) net-imap (0.5.12) @@ -160,22 +174,6 @@ GEM nokogiri (1.18.10) mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.18.10-aarch64-linux-gnu) - racc (~> 1.4) - nokogiri (1.18.10-aarch64-linux-musl) - racc (~> 1.4) - nokogiri (1.18.10-arm-linux-gnu) - racc (~> 1.4) - nokogiri (1.18.10-arm-linux-musl) - racc (~> 1.4) - nokogiri (1.18.10-arm64-darwin) - racc (~> 1.4) - nokogiri (1.18.10-x86_64-darwin) - racc (~> 1.4) - nokogiri (1.18.10-x86_64-linux-gnu) - racc (~> 1.4) - nokogiri (1.18.10-x86_64-linux-musl) - racc (~> 1.4) openssl (4.0.2) parallel (1.27.0) parser (3.3.8.0) @@ -244,7 +242,7 @@ GEM reline (0.6.3) io-console (~> 0.5) rexml (3.4.4) - rubocop (1.78.0) + rubocop (1.80.2) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) @@ -252,7 +250,7 @@ GEM parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) - rubocop-ast (>= 1.45.1, < 2.0) + rubocop-ast (>= 1.46.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) rubocop-ast (1.46.0) @@ -292,9 +290,9 @@ GEM tsort (0.2.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (3.1.4) - unicode-emoji (~> 4.0, >= 4.0.4) - unicode-emoji (4.0.4) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.2.0) uri (1.1.1) useragent (0.16.11) webmock (3.26.1) diff --git a/fasp_base/fasp_base.gemspec b/fasp_base/fasp_base.gemspec index cfe39e6..b33b495 100644 --- a/fasp_base/fasp_base.gemspec +++ b/fasp_base/fasp_base.gemspec @@ -22,6 +22,8 @@ Gem::Specification.new do |spec| spec.add_dependency "httpx" spec.add_dependency "linzer", ">= 0.7.7" spec.add_dependency "openssl" + spec.add_dependency "addressable" + spec.add_dependency "idnx" spec.add_development_dependency "webmock" end diff --git a/fasp_data_sharing/Gemfile b/fasp_data_sharing/Gemfile index 1521e64..5ea621b 100644 --- a/fasp_data_sharing/Gemfile +++ b/fasp_data_sharing/Gemfile @@ -9,9 +9,6 @@ gem "sqlite3" gem "propshaft" -gem "addressable" -gem "idnx" - # Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/] gem "rubocop-rails-omakase", require: false diff --git a/fasp_data_sharing/Gemfile.lock b/fasp_data_sharing/Gemfile.lock index 8c5f453..3dc128e 100644 --- a/fasp_data_sharing/Gemfile.lock +++ b/fasp_data_sharing/Gemfile.lock @@ -2,8 +2,10 @@ PATH remote: ../fasp_base specs: fasp_base (0.1.0) + addressable bcrypt httpx + idnx linzer (>= 0.7.7) openssl rails (>= 8.0.0) @@ -165,7 +167,7 @@ GEM marcel (1.1.0) mini_mime (1.1.5) mini_portile2 (2.8.9) - minitest (5.26.0) + minitest (5.27.0) net-http (0.6.0) uri net-imap (0.5.12) @@ -181,22 +183,6 @@ GEM nokogiri (1.18.10) mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.18.10-aarch64-linux-gnu) - racc (~> 1.4) - nokogiri (1.18.10-aarch64-linux-musl) - racc (~> 1.4) - nokogiri (1.18.10-arm-linux-gnu) - racc (~> 1.4) - nokogiri (1.18.10-arm-linux-musl) - racc (~> 1.4) - nokogiri (1.18.10-arm64-darwin) - racc (~> 1.4) - nokogiri (1.18.10-x86_64-darwin) - racc (~> 1.4) - nokogiri (1.18.10-x86_64-linux-gnu) - racc (~> 1.4) - nokogiri (1.18.10-x86_64-linux-musl) - racc (~> 1.4) openssl (3.3.2) parallel (1.27.0) parser (3.3.10.0) @@ -295,16 +281,8 @@ GEM rubocop-rails (>= 2.30) ruby-progressbar (1.13.0) securerandom (0.4.1) - sqlite3 (2.7.4-aarch64-linux-gnu) - sqlite3 (2.7.4-aarch64-linux-musl) - sqlite3 (2.7.4-arm-linux-gnu) - sqlite3 (2.7.4-arm-linux-musl) - sqlite3 (2.7.4-arm64-darwin) - sqlite3 (2.7.4-x86-linux-gnu) - sqlite3 (2.7.4-x86-linux-musl) - sqlite3 (2.7.4-x86_64-darwin) - sqlite3 (2.7.4-x86_64-linux-gnu) - sqlite3 (2.7.4-x86_64-linux-musl) + sqlite3 (2.7.4) + mini_portile2 (~> 2.8.0) starry (0.2.0) base64 stringio (3.1.7) @@ -315,7 +293,7 @@ GEM concurrent-ruby (~> 1.0) unicode-display_width (3.2.0) unicode-emoji (~> 4.1) - unicode-emoji (4.1.0) + unicode-emoji (4.2.0) uri (1.1.1) useragent (0.16.11) webmock (3.26.1) @@ -344,11 +322,9 @@ PLATFORMS x86_64-linux-musl DEPENDENCIES - addressable debug (>= 1.0.0) fasp_base! fasp_data_sharing! - idnx propshaft puma rubocop-rails-omakase diff --git a/fasp_data_sharing/app/models/fasp_data_sharing/activity_pub_object.rb b/fasp_data_sharing/app/models/fasp_data_sharing/activity_pub_object.rb index 0e93b59..bda772d 100644 --- a/fasp_data_sharing/app/models/fasp_data_sharing/activity_pub_object.rb +++ b/fasp_data_sharing/app/models/fasp_data_sharing/activity_pub_object.rb @@ -52,7 +52,7 @@ def signature_headers end def sign(headers) - parsed_uri = Addressable::URI.parse(uri).normalize + parsed_uri = URI(uri) target = parsed_uri.path target << "?#{parsed_uri.query}" unless parsed_uri.query.nil? string = [ diff --git a/fasp_data_sharing/test/models/fasp_data_sharing/activity_pub_object_test.rb b/fasp_data_sharing/test/models/fasp_data_sharing/activity_pub_object_test.rb index 91ffdc2..ceac0d0 100644 --- a/fasp_data_sharing/test/models/fasp_data_sharing/activity_pub_object_test.rb +++ b/fasp_data_sharing/test/models/fasp_data_sharing/activity_pub_object_test.rb @@ -43,7 +43,7 @@ class ActivityPubObjectTest < ActiveSupport::TestCase assert_requested(signatures_stub) end - test "when URI contains non-ASCII characters it gets normalized" do + test "when URI contains non-ASCII characters it gets parsed and processed successfully" do uri = "https://other.example.com/users/\u4F11\u65E5\u8AB2\u9577" json_object = { "@context" => "https://www.w3.org/ns/activitystreams", From 03371b3e43bab3cc456bfbe4145fa4a09213eccb Mon Sep 17 00:00:00 2001 From: Pia B Date: Mon, 10 Aug 2026 15:45:27 +0200 Subject: [PATCH 5/6] add addressable for parsing in sign headers --- .../app/models/fasp_data_sharing/activity_pub_object.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fasp_data_sharing/app/models/fasp_data_sharing/activity_pub_object.rb b/fasp_data_sharing/app/models/fasp_data_sharing/activity_pub_object.rb index bda772d..e1f9d95 100644 --- a/fasp_data_sharing/app/models/fasp_data_sharing/activity_pub_object.rb +++ b/fasp_data_sharing/app/models/fasp_data_sharing/activity_pub_object.rb @@ -52,7 +52,7 @@ def signature_headers end def sign(headers) - parsed_uri = URI(uri) + parsed_uri = Addressable::URI.parse(uri) target = parsed_uri.path target << "?#{parsed_uri.query}" unless parsed_uri.query.nil? string = [ From 2e848261a7db904a13c449abf05f63c407801402 Mon Sep 17 00:00:00 2001 From: Pia B Date: Mon, 10 Aug 2026 17:27:10 +0200 Subject: [PATCH 6/6] add method for parsed_uri --- .../models/fasp_data_sharing/activity_pub_object.rb | 11 ++++++----- .../fasp_data_sharing/activity_pub_object_test.rb | 5 ++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/fasp_data_sharing/app/models/fasp_data_sharing/activity_pub_object.rb b/fasp_data_sharing/app/models/fasp_data_sharing/activity_pub_object.rb index e1f9d95..6a1078e 100644 --- a/fasp_data_sharing/app/models/fasp_data_sharing/activity_pub_object.rb +++ b/fasp_data_sharing/app/models/fasp_data_sharing/activity_pub_object.rb @@ -40,7 +40,7 @@ def signature_headers "accept" => "application/activity+json", "digest" => "SHA-256=#{EMPTY_DIGEST}", "date" => Time.now.utc.httpdate, - "host" => URI(uri).host + "host" => parsed_uri.host } signature_header = [ "keyId=\"#{keyid}\"", @@ -52,11 +52,8 @@ def signature_headers end def sign(headers) - parsed_uri = Addressable::URI.parse(uri) - target = parsed_uri.path - target << "?#{parsed_uri.query}" unless parsed_uri.query.nil? string = [ - "(request-target): get #{target}", + "(request-target): get #{parsed_uri.request_uri}", "date: #{headers["date"]}", "digest: #{headers["digest"]}", "host: #{headers["host"]}" @@ -72,4 +69,8 @@ def keyid def private_key_pem @private_key_pem ||= FaspDataSharing::Actor.instance.private_key_pem end + + def parsed_uri + @parsed_uri ||= Addressable::URI.parse(uri) + end end diff --git a/fasp_data_sharing/test/models/fasp_data_sharing/activity_pub_object_test.rb b/fasp_data_sharing/test/models/fasp_data_sharing/activity_pub_object_test.rb index ceac0d0..999a80c 100644 --- a/fasp_data_sharing/test/models/fasp_data_sharing/activity_pub_object_test.rb +++ b/fasp_data_sharing/test/models/fasp_data_sharing/activity_pub_object_test.rb @@ -44,13 +44,13 @@ class ActivityPubObjectTest < ActiveSupport::TestCase end test "when URI contains non-ASCII characters it gets parsed and processed successfully" do - uri = "https://other.example.com/users/\u4F11\u65E5\u8AB2\u9577" + uri = "http://www.詹姆斯.com/users/\u4F11\u65E5\u8AB2\u9577" json_object = { "@context" => "https://www.w3.org/ns/activitystreams", "id" => uri } activity_pub_object = ActivityPubObject.new(uri: uri) - message_signatures_stub = stub_request(:get, uri) + message_signatures_stub = stub_request(:get, "http://www.xn--8ws00zhy3a.com/users/%E4%BC%91%E6%97%A5%E8%AA%B2%E9%95%B7") .with { |r| r.headers["Signature-Input"].present? } .to_return_json( body: json_object, @@ -58,7 +58,6 @@ class ActivityPubObjectTest < ActiveSupport::TestCase }) returned_json = activity_pub_object.fetch - assert_equal json_object, returned_json assert_requested(message_signatures_stub) end