Support more variations of ActivityPub keyId in signature (#4630)

- Tries to avoid performing HTTP request if the keyId is an actor URI
- Likewise if the URI is a fragment URI on top of actor URI
- Resolves public key, returns owner if the owner links back to the key
master
Eugen Rochko 7 years ago committed by GitHub
parent f391a4673a
commit 72bb3e03fd
  1. 4
      app/controllers/concerns/signature_verification.rb
  2. 6
      app/helpers/jsonld_helper.rb
  3. 2
      app/lib/activitypub/activity.rb
  4. 2
      app/lib/activitypub/activity/accept.rb
  5. 2
      app/lib/activitypub/activity/reject.rb
  6. 2
      app/lib/activitypub/activity/undo.rb
  7. 2
      app/lib/activitypub/tag_manager.rb
  8. 47
      app/services/activitypub/fetch_remote_key_service.rb

@ -98,7 +98,9 @@ module SignatureVerification
if key_id.start_with?('acct:')
ResolveRemoteAccountService.new.call(key_id.gsub(/\Aacct:/, ''))
elsif !ActivityPub::TagManager.instance.local_uri?(key_id)
ActivityPub::FetchRemoteAccountService.new.call(key_id)
account = ActivityPub::TagManager.instance.uri_to_resource(key_id, Account)
account ||= ActivityPub::FetchRemoteKeyService.new.call(key_id)
account
end
end
end

@ -9,6 +9,10 @@ module JsonLdHelper
value.is_a?(Array) ? value.first : value
end
def value_or_id(value)
value.is_a?(String) ? value : value['id']
end
def supported_context?(json)
equals_or_includes?(json['@context'], ActivityPub::TagManager::CONTEXT)
end
@ -20,7 +24,7 @@ module JsonLdHelper
end
def body_to_json(body)
body.nil? ? nil : Oj.load(body, mode: :strict)
body.is_a?(String) ? Oj.load(body, mode: :strict) : body
rescue Oj::ParseError
nil
end

@ -58,7 +58,7 @@ class ActivityPub::Activity
end
def object_uri
@object_uri ||= @object.is_a?(String) ? @object : @object['id']
@object_uri ||= value_or_id(@object)
end
def redis

@ -20,6 +20,6 @@ class ActivityPub::Activity::Accept < ActivityPub::Activity
end
def target_uri
@target_uri ||= @object['actor']
@target_uri ||= value_or_id(@object['actor'])
end
end

@ -20,6 +20,6 @@ class ActivityPub::Activity::Reject < ActivityPub::Activity
end
def target_uri
@target_uri ||= @object['actor']
@target_uri ||= value_or_id(@object['actor'])
end
end

@ -64,6 +64,6 @@ class ActivityPub::Activity::Undo < ActivityPub::Activity
end
def target_uri
@target_uri ||= @object['object'].is_a?(String) ? @object['object'] : @object['object']['id']
@target_uri ||= value_or_id(@object['object'])
end
end

@ -93,7 +93,7 @@ class ActivityPub::TagManager
elsif ::TagManager.instance.local_id?(uri)
klass.find_by(id: ::TagManager.instance.unique_tag_to_local_id(uri, klass.to_s))
else
klass.find_by(uri: uri)
klass.find_by(uri: uri.split('#').first)
end
end
end

@ -0,0 +1,47 @@
# frozen_string_literal: true
class ActivityPub::FetchRemoteKeyService < BaseService
include JsonLdHelper
# Returns account that owns the key
def call(uri, prefetched_json = nil)
@json = body_to_json(prefetched_json) || fetch_resource(uri)
return unless supported_context?(@json) && expected_type?
return find_account(uri, @json) if person?
@owner = fetch_resource(owner_uri)
return unless supported_context?(@owner) && confirmed_owner?
find_account(owner_uri, @owner)
end
private
def find_account(uri, prefetched_json)
account = ActivityPub::TagManager.instance.uri_to_resource(uri, Account)
account ||= ActivityPub::FetchRemoteAccountService.new.call(uri, prefetched_json)
account
end
def expected_type?
person? || public_key?
end
def person?
@json['type'] == 'Person'
end
def public_key?
@json['publicKeyPem'].present? && @json['owner'].present?
end
def owner_uri
@owner_uri ||= value_or_id(@json['owner'])
end
def confirmed_owner?
@owner['type'] == 'Person' && value_or_id(@owner['publicKey']) == @json['id']
end
end
Loading…
Cancel
Save