Merge branch 'master' into glitch-soc/merge-upstream

Conflicts:
- `app/controllers/accounts_controller.rb`:
  Upstream change too close to a glitch-soc change related to
  instance-local toots. Merged upstream changes.
- `app/services/fan_out_on_write_service.rb`:
  Minor conflict due to glitch-soc's handling of Direct Messages,
  merged upstream changes.
- `yarn.lock`:
  Not really a conflict, caused by glitch-soc-only dependencies
  being textually too close to updated upstream dependencies.
  Merged upstream changes.
This commit is contained in:
Thibaut Girka
2020-08-30 16:13:08 +02:00
109 changed files with 2948 additions and 1238 deletions

View File

@@ -71,7 +71,15 @@ class ActivityPub::Activity
end
def object_uri
@object_uri ||= value_or_id(@object)
@object_uri ||= begin
str = value_or_id(@object)
if str.start_with?('bear:')
Addressable::URI.parse(str).query_values['u']
else
str
end
end
end
def unsupported_object_type?
@@ -159,20 +167,20 @@ class ActivityPub::Activity
def dereference_object!
return unless @object.is_a?(String)
return if invalid_origin?(@object)
object = fetch_resource(@object, true, signed_fetch_account)
return unless object.present? && object.is_a?(Hash) && supported_context?(object)
dereferencer = ActivityPub::Dereferencer.new(@object, permitted_origin: @account.uri, signature_account: signed_fetch_account)
@object = object
@object = dereferencer.object unless dereferencer.object.nil?
end
def signed_fetch_account
return Account.find(@options[:delivered_to_account_id]) if @options[:delivered_to_account_id].present?
first_mentioned_local_account || first_local_follower
end
def first_mentioned_local_account
audience = (as_array(@json['to']) + as_array(@json['cc'])).uniq
audience = (as_array(@json['to']) + as_array(@json['cc'])).map { |x| value_or_id(x) }.uniq
local_usernames = audience.select { |uri| ActivityPub::TagManager.instance.local_uri?(uri) }
.map { |uri| ActivityPub::TagManager.instance.uri_to_local_id(uri, :username) }

View File

@@ -34,12 +34,20 @@ class ActivityPub::Activity::Announce < ActivityPub::Activity
private
def audience_to
as_array(@json['to']).map { |x| value_or_id(x) }
end
def audience_cc
as_array(@json['cc']).map { |x| value_or_id(x) }
end
def visibility_from_audience
if equals_or_includes?(@json['to'], ActivityPub::TagManager::COLLECTIONS[:public])
if audience_to.include?(ActivityPub::TagManager::COLLECTIONS[:public])
:public
elsif equals_or_includes?(@json['cc'], ActivityPub::TagManager::COLLECTIONS[:public])
elsif audience_cc.include?(ActivityPub::TagManager::COLLECTIONS[:public])
:unlisted
elsif equals_or_includes?(@json['to'], @account.followers_url)
elsif audience_to.include?(@account.followers_url)
:private
else
:direct

View File

@@ -15,7 +15,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
private
def create_encrypted_message
return reject_payload! if invalid_origin?(@object['id']) || @options[:delivered_to_account_id].blank?
return reject_payload! if invalid_origin?(object_uri) || @options[:delivered_to_account_id].blank?
target_account = Account.find(@options[:delivered_to_account_id])
target_device = target_account.devices.find_by(device_id: @object.dig('to', 'deviceId'))
@@ -43,7 +43,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
end
def create_status
return reject_payload! if unsupported_object_type? || invalid_origin?(@object['id']) || Tombstone.exists?(uri: @object['id']) || !related_to_local_activity?
return reject_payload! if unsupported_object_type? || invalid_origin?(object_uri) || tombstone_exists? || !related_to_local_activity?
RedisLock.acquire(lock_options) do |lock|
if lock.acquired?
@@ -65,11 +65,11 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
end
def audience_to
@object['to'] || @json['to']
as_array(@object['to'] || @json['to']).map { |x| value_or_id(x) }
end
def audience_cc
@object['cc'] || @json['cc']
as_array(@object['cc'] || @json['cc']).map { |x| value_or_id(x) }
end
def process_status
@@ -90,7 +90,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
fetch_replies(@status)
check_for_spam
distribute(@status)
forward_for_reply if @status.distributable?
forward_for_reply
end
def find_existing_status
@@ -102,8 +102,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
def process_status_params
@params = begin
{
uri: @object['id'],
url: object_url || @object['id'],
uri: object_uri,
url: object_url || object_uri,
account: @account,
text: text_from_content || '',
language: detected_language,
@@ -122,7 +122,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
end
def process_audience
(as_array(audience_to) + as_array(audience_cc)).uniq.each do |audience|
(audience_to + audience_cc).uniq.each do |audience|
next if audience == ActivityPub::TagManager::COLLECTIONS[:public]
# Unlike with tags, there is no point in resolving accounts we don't already
@@ -313,7 +313,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
RedisLock.acquire(poll_lock_options) do |lock|
if lock.acquired?
already_voted = poll.votes.where(account: @account).exists?
poll.votes.create!(account: @account, choice: poll.options.index(@object['name']), uri: @object['id'])
poll.votes.create!(account: @account, choice: poll.options.index(@object['name']), uri: object_uri)
else
raise Mastodon::RaceConditionError
end
@@ -352,11 +352,11 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
end
def visibility_from_audience
if equals_or_includes?(audience_to, ActivityPub::TagManager::COLLECTIONS[:public])
if audience_to.include?(ActivityPub::TagManager::COLLECTIONS[:public])
:public
elsif equals_or_includes?(audience_cc, ActivityPub::TagManager::COLLECTIONS[:public])
elsif audience_cc.include?(ActivityPub::TagManager::COLLECTIONS[:public])
:unlisted
elsif equals_or_includes?(audience_to, @account.followers_url)
elsif audience_to.include?(@account.followers_url)
:private
elsif direct_message == false
:limited
@@ -367,7 +367,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
def audience_includes?(account)
uri = ActivityPub::TagManager.instance.uri_for(account)
equals_or_includes?(audience_to, uri) || equals_or_includes?(audience_cc, uri)
audience_to.include?(uri) || audience_cc.include?(uri)
end
def direct_message
@@ -391,7 +391,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
end
def text_from_content
return Formatter.instance.linkify([[text_from_name, text_from_summary.presence].compact.join("\n\n"), object_url || @object['id']].join(' ')) if converted_object_type?
return Formatter.instance.linkify([[text_from_name, text_from_summary.presence].compact.join("\n\n"), object_url || object_uri].join(' ')) if converted_object_type?
if @object['content'].present?
@object['content']
@@ -483,19 +483,23 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
def addresses_local_accounts?
return true if @options[:delivered_to_account_id]
local_usernames = (as_array(audience_to) + as_array(audience_cc)).uniq.select { |uri| ActivityPub::TagManager.instance.local_uri?(uri) }.map { |uri| ActivityPub::TagManager.instance.uri_to_local_id(uri, :username) }
local_usernames = (audience_to + audience_cc).uniq.select { |uri| ActivityPub::TagManager.instance.local_uri?(uri) }.map { |uri| ActivityPub::TagManager.instance.uri_to_local_id(uri, :username) }
return false if local_usernames.empty?
Account.local.where(username: local_usernames).exists?
end
def tombstone_exists?
Tombstone.exists?(uri: object_uri)
end
def check_for_spam
SpamCheck.perform(@status)
end
def forward_for_reply
return unless @json['signature'].present? && reply_to_local?
return unless @status.distributable? && @json['signature'].present? && reply_to_local?
ActivityPub::RawDistributionWorker.perform_async(Oj.dump(@json), replied_to_status.account_id, [@account.preferred_inbox_url])
end
@@ -513,7 +517,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
end
def lock_options
{ redis: Redis.current, key: "create:#{@object['id']}" }
{ redis: Redis.current, key: "create:#{object_uri}" }
end
def poll_lock_options

View File

@@ -0,0 +1,69 @@
# frozen_string_literal: true
class ActivityPub::Dereferencer
include JsonLdHelper
def initialize(uri, permitted_origin: nil, signature_account: nil)
@uri = uri
@permitted_origin = permitted_origin
@signature_account = signature_account
end
def object
@object ||= fetch_object!
end
private
def bear_cap?
@uri.start_with?('bear:')
end
def fetch_object!
if bear_cap?
fetch_with_token!
else
fetch_with_signature!
end
end
def fetch_with_token!
perform_request(bear_cap['u'], headers: { 'Authorization' => "Bearer #{bear_cap['t']}" })
end
def fetch_with_signature!
perform_request(@uri)
end
def bear_cap
@bear_cap ||= Addressable::URI.parse(@uri).query_values
end
def perform_request(uri, headers: nil)
return if invalid_origin?(uri)
req = Request.new(:get, uri)
req.add_headers('Accept' => 'application/activity+json, application/ld+json')
req.add_headers(headers) if headers
req.on_behalf_of(@signature_account) if @signature_account
req.perform do |res|
if res.code == 200
json = body_to_json(res.body_with_limit)
json if json.present? && json['id'] == uri
else
raise Mastodon::UnexpectedResponseError, res unless response_successful?(res) || response_error_unsalvageable?(res)
end
end
end
def invalid_origin?(uri)
return true if unsupported_uri_scheme?(uri)
needle = Addressable::URI.parse(uri).host
haystack = Addressable::URI.parse(@permitted_origin).host
!haystack.casecmp(needle).zero?
end
end