mirror of
https://github.com/glitch-soc/mastodon.git
synced 2026-03-29 03:00:33 +02:00
Add support for FEP-2c59 (#38239)
This commit is contained in:
@@ -4,6 +4,7 @@ module ContextHelper
|
||||
NAMED_CONTEXT_MAP = {
|
||||
activitystreams: 'https://www.w3.org/ns/activitystreams',
|
||||
security: 'https://w3id.org/security/v1',
|
||||
webfinger: 'https://purl.archive.org/socialweb/webfinger',
|
||||
}.freeze
|
||||
|
||||
CONTEXT_EXTENSION_MAP = {
|
||||
|
||||
@@ -4,7 +4,7 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
|
||||
include RoutingHelper
|
||||
include FormattingHelper
|
||||
|
||||
context :security
|
||||
context :security, :webfinger
|
||||
|
||||
context_extensions :manually_approves_followers, :featured, :also_known_as,
|
||||
:moved_to, :property_value, :discoverable, :suspended,
|
||||
@@ -55,6 +55,10 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
|
||||
ActivityPub::TagManager.instance.uri_for(object)
|
||||
end
|
||||
|
||||
def webfinger
|
||||
object.local_username_and_domain
|
||||
end
|
||||
|
||||
def type
|
||||
if object.instance_actor?
|
||||
'Application'
|
||||
|
||||
@@ -27,11 +27,23 @@ class ActivityPub::FetchRemoteActorService < BaseService
|
||||
raise Error, "Unsupported JSON-LD context for document #{uri}" unless supported_context?
|
||||
raise Error, "Unexpected object type for actor #{uri} (expected any of: #{SUPPORTED_TYPES})" unless expected_type?
|
||||
raise Error, "Actor #{uri} has moved to #{@json['movedTo']}" if break_on_redirect && @json['movedTo'].present?
|
||||
raise Error, "Actor #{uri} has no 'preferredUsername', which is a requirement for Mastodon compatibility" if @json['preferredUsername'].blank?
|
||||
raise Error, "Actor #{uri} has neither 'preferredUsername' nor `webfinger`, which is a requirement for Mastodon compatibility" if @json['preferredUsername'].blank? && @json['webfinger'].blank?
|
||||
|
||||
@uri = @json['id']
|
||||
@username = @json['preferredUsername']
|
||||
@domain = Addressable::URI.parse(@uri).normalized_host
|
||||
@uri = @json['id']
|
||||
|
||||
# FEP-2c59 defines a `webfinger` attribute that makes things more explicit and spares an extra request in some cases.
|
||||
# It supersedes `preferredUsername`.
|
||||
if @json['webfinger'].present? && @json['webfinger'].is_a?(String)
|
||||
@username, @domain = split_acct(@json['webfinger'])
|
||||
Rails.logger.debug { "Actor #{uri} has an invalid `webfinger` value, falling back to `preferredUsername`" }
|
||||
end
|
||||
|
||||
if @username.blank? || @domain.blank?
|
||||
raise "Actor #{uri} has no `preferredUsername`, and either a bogus or missing `webfinger`, which is a requirement for Mastodon compatibility" if @json['preferredUsername'].blank?
|
||||
|
||||
@username = @json['preferredUsername']
|
||||
@domain = Addressable::URI.parse(@uri).normalized_host
|
||||
end
|
||||
|
||||
check_webfinger! unless only_key
|
||||
|
||||
|
||||
14
config/initializers/json_ld_webfinger.rb
Normal file
14
config/initializers/json_ld_webfinger.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'json/ld'
|
||||
|
||||
class JSON::LD::Context
|
||||
add_preloaded("http://purl.archive.org/socialweb/webfinger") do
|
||||
new(processingMode: "json-ld-1.0", term_definitions: {
|
||||
"webfinger" => TermDefinition.new("webfinger", id: "https://purl.archive.org/socialweb/webfinger#webfinger", type_mapping: "http://www.w3.org/2001/XMLSchema#string"),
|
||||
"wf" => TermDefinition.new("wf", id: "https://purl.archive.org/socialweb/webfinger#", simple: true, prefix: true),
|
||||
"xsd" => TermDefinition.new("xsd", id: "http://www.w3.org/2001/XMLSchema#", simple: true, prefix: true)
|
||||
})
|
||||
end
|
||||
alias_preloaded("https://purl.archive.org/socialweb/webfinger", "http://purl.archive.org/socialweb/webfinger")
|
||||
end
|
||||
@@ -133,5 +133,97 @@ RSpec.describe ActivityPub::FetchRemoteActorService do
|
||||
expect(subject.call('https://fake.address/@foo', prefetched_body: actor.to_json)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the actor uses the webfinger propery from FEP-2c59' do
|
||||
before do
|
||||
actor[:webfinger] = acct
|
||||
end
|
||||
|
||||
context 'when URI and WebFinger share the same host' do
|
||||
let(:acct) { 'alice@example.com' }
|
||||
let!(:webfinger) { { subject: "acct:#{acct}", links: [{ rel: 'self', href: 'https://example.com/alice', type: 'application/activity+json' }] } }
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: actor.to_json, headers: { 'Content-Type': 'application/activity+json' })
|
||||
stub_request(:get, "https://example.com/.well-known/webfinger?resource=acct:#{acct}").to_return(body: webfinger.to_json, headers: { 'Content-Type': 'application/jrd+json' })
|
||||
end
|
||||
|
||||
it 'fetches resource and looks up webfinger and sets values' do
|
||||
account
|
||||
|
||||
expect(a_request(:get, 'https://example.com/alice')).to have_been_made.once
|
||||
expect(a_request(:get, "https://example.com/.well-known/webfinger?resource=acct:#{acct}")).to have_been_made.once
|
||||
|
||||
expect(account.username).to eq 'alice'
|
||||
expect(account.domain).to eq 'example.com'
|
||||
end
|
||||
|
||||
it_behaves_like 'sets profile data'
|
||||
end
|
||||
|
||||
context 'when WebFinger returns a different URI' do
|
||||
let(:acct) { 'alice@example.com' }
|
||||
let!(:webfinger) { { subject: "acct:#{acct}", links: [{ rel: 'self', href: 'https://example.com/bob', type: 'application/activity+json' }] } }
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: actor.to_json, headers: { 'Content-Type': 'application/activity+json' })
|
||||
stub_request(:get, "https://example.com/.well-known/webfinger?resource=acct:#{acct}").to_return(body: webfinger.to_json, headers: { 'Content-Type': 'application/jrd+json' })
|
||||
end
|
||||
|
||||
it 'fetches resource and looks up webfinger and does not create account' do
|
||||
expect(account).to be_nil
|
||||
|
||||
expect(a_request(:get, 'https://example.com/alice')).to have_been_made.once
|
||||
expect(a_request(:get, "https://example.com/.well-known/webfinger?resource=acct:#{acct}")).to have_been_made.once
|
||||
end
|
||||
end
|
||||
|
||||
context 'when WebFinger is at another domain' do
|
||||
let(:acct) { 'alice@iscool.af' }
|
||||
let!(:webfinger) { { subject: "acct:#{acct}", links: [{ rel: 'self', href: 'https://example.com/alice', type: 'application/activity+json' }] } }
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: actor.to_json, headers: { 'Content-Type': 'application/activity+json' })
|
||||
stub_request(:get, "https://iscool.af/.well-known/webfinger?resource=acct:#{acct}").to_return(body: webfinger.to_json, headers: { 'Content-Type': 'application/jrd+json' })
|
||||
end
|
||||
|
||||
it 'fetches resource and looks up webfinger and follows redirect and sets values' do
|
||||
account
|
||||
|
||||
expect(a_request(:get, 'https://example.com/alice')).to have_been_made.once
|
||||
expect(a_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com')).to_not have_been_made
|
||||
expect(a_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af')).to have_been_made.once
|
||||
|
||||
expect(account.username).to eq 'alice'
|
||||
expect(account.domain).to eq 'iscool.af'
|
||||
end
|
||||
|
||||
it_behaves_like 'sets profile data'
|
||||
end
|
||||
|
||||
context 'when WebFinger is at another domain and redirects back' do
|
||||
let(:acct) { 'alice@iscool.af' }
|
||||
let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice', type: 'application/activity+json' }] } }
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: actor.to_json, headers: { 'Content-Type': 'application/activity+json' })
|
||||
stub_request(:get, "https://iscool.af/.well-known/webfinger?resource=acct:#{acct}").to_return(body: webfinger.to_json, headers: { 'Content-Type': 'application/jrd+json' })
|
||||
stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: webfinger.to_json, headers: { 'Content-Type': 'application/jrd+json' })
|
||||
end
|
||||
|
||||
it 'fetches resource and looks up webfinger and follows redirect and sets values' do
|
||||
account
|
||||
|
||||
expect(a_request(:get, 'https://example.com/alice')).to have_been_made.once
|
||||
expect(a_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af')).to have_been_made.once
|
||||
expect(a_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com')).to have_been_made
|
||||
|
||||
expect(account.username).to eq 'alice'
|
||||
expect(account.domain).to eq 'example.com'
|
||||
end
|
||||
|
||||
it_behaves_like 'sets profile data'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user