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 = {
|
NAMED_CONTEXT_MAP = {
|
||||||
activitystreams: 'https://www.w3.org/ns/activitystreams',
|
activitystreams: 'https://www.w3.org/ns/activitystreams',
|
||||||
security: 'https://w3id.org/security/v1',
|
security: 'https://w3id.org/security/v1',
|
||||||
|
webfinger: 'https://purl.archive.org/socialweb/webfinger',
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
CONTEXT_EXTENSION_MAP = {
|
CONTEXT_EXTENSION_MAP = {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
|
|||||||
include RoutingHelper
|
include RoutingHelper
|
||||||
include FormattingHelper
|
include FormattingHelper
|
||||||
|
|
||||||
context :security
|
context :security, :webfinger
|
||||||
|
|
||||||
context_extensions :manually_approves_followers, :featured, :also_known_as,
|
context_extensions :manually_approves_followers, :featured, :also_known_as,
|
||||||
:moved_to, :property_value, :discoverable, :suspended,
|
:moved_to, :property_value, :discoverable, :suspended,
|
||||||
@@ -55,6 +55,10 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
|
|||||||
ActivityPub::TagManager.instance.uri_for(object)
|
ActivityPub::TagManager.instance.uri_for(object)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def webfinger
|
||||||
|
object.local_username_and_domain
|
||||||
|
end
|
||||||
|
|
||||||
def type
|
def type
|
||||||
if object.instance_actor?
|
if object.instance_actor?
|
||||||
'Application'
|
'Application'
|
||||||
|
|||||||
@@ -27,11 +27,23 @@ class ActivityPub::FetchRemoteActorService < BaseService
|
|||||||
raise Error, "Unsupported JSON-LD context for document #{uri}" unless supported_context?
|
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, "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 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']
|
@uri = @json['id']
|
||||||
@username = @json['preferredUsername']
|
|
||||||
@domain = Addressable::URI.parse(@uri).normalized_host
|
# 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
|
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
|
expect(subject.call('https://fake.address/@foo', prefetched_body: actor.to_json)).to be_nil
|
||||||
end
|
end
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user