From 5d8398c8b8b51ee7363e7d45acc560f489783e34 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 2 Jun 2020 19:24:53 +0200 Subject: Add E2EE API (#13820) --- app/lib/activitypub/activity/create.rb | 50 ++++++++++++++++++++++++++++++++-- app/lib/activitypub/adapter.rb | 1 + app/lib/inline_renderer.rb | 2 ++ 3 files changed, 51 insertions(+), 2 deletions(-) (limited to 'app/lib') diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index 572b8087e..3509a6c40 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -2,6 +2,45 @@ class ActivityPub::Activity::Create < ActivityPub::Activity def perform + case @object['type'] + when 'EncryptedMessage' + create_encrypted_message + else + create_status + end + end + + private + + def create_encrypted_message + return reject_payload! if invalid_origin?(@object['id']) || @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')) + + return if target_device.nil? + + target_device.encrypted_messages.create!( + from_account: @account, + from_device_id: @object.dig('attributedTo', 'deviceId'), + type: @object['messageType'], + body: @object['cipherText'], + digest: @object.dig('digest', 'digestValue'), + message_franking: message_franking.to_token + ) + end + + def message_franking + MessageFranking.new( + hmac: @object.dig('digest', 'digestValue'), + original_franking: @object['messageFranking'], + source_account_id: @account.id, + target_account_id: @options[:delivered_to_account_id], + timestamp: Time.now.utc + ) + end + + def create_status return reject_payload! if unsupported_object_type? || invalid_origin?(@object['id']) || Tombstone.exists?(uri: @object['id']) || !related_to_local_activity? RedisLock.acquire(lock_options) do |lock| @@ -23,8 +62,6 @@ class ActivityPub::Activity::Create < ActivityPub::Activity @status end - private - def audience_to @object['to'] || @json['to'] end @@ -262,6 +299,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity def poll_vote! poll = replied_to_status.preloadable_poll already_voted = true + RedisLock.acquire(poll_lock_options) do |lock| if lock.acquired? already_voted = poll.votes.where(account: @account).exists? @@ -270,20 +308,24 @@ class ActivityPub::Activity::Create < ActivityPub::Activity raise Mastodon::RaceConditionError end end + increment_voters_count! unless already_voted ActivityPub::DistributePollUpdateWorker.perform_in(3.minutes, replied_to_status.id) unless replied_to_status.preloadable_poll.hide_totals? end def resolve_thread(status) return unless status.reply? && status.thread.nil? && Request.valid_url?(in_reply_to_uri) + ThreadResolveWorker.perform_async(status.id, in_reply_to_uri) end def fetch_replies(status) collection = @object['replies'] return if collection.nil? + replies = ActivityPub::FetchRepliesService.new.call(status, collection, false) return unless replies.nil? + uri = value_or_id(collection) ActivityPub::FetchRepliesWorker.perform_async(status.id, uri) unless uri.nil? end @@ -291,6 +333,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity def conversation_from_uri(uri) return nil if uri.nil? return Conversation.find_by(id: OStatus::TagManager.instance.unique_tag_to_local_id(uri, 'Conversation')) if OStatus::TagManager.instance.local_id?(uri) + begin Conversation.find_or_create_by!(uri: uri) rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique @@ -404,6 +447,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity def skip_download? return @skip_download if defined?(@skip_download) + @skip_download ||= DomainBlock.reject_media?(@account.domain) end @@ -436,11 +480,13 @@ class ActivityPub::Activity::Create < ActivityPub::Activity def forward_for_reply return unless @json['signature'].present? && reply_to_local? + ActivityPub::RawDistributionWorker.perform_async(Oj.dump(@json), replied_to_status.account_id, [@account.preferred_inbox_url]) end def increment_voters_count! poll = replied_to_status.preloadable_poll + unless poll.voters_count.nil? poll.voters_count = poll.voters_count + 1 poll.save diff --git a/app/lib/activitypub/adapter.rb b/app/lib/activitypub/adapter.rb index 78138fb73..634ed29fa 100644 --- a/app/lib/activitypub/adapter.rb +++ b/app/lib/activitypub/adapter.rb @@ -22,6 +22,7 @@ class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base blurhash: { 'toot' => 'http://joinmastodon.org/ns#', 'blurhash' => 'toot:blurhash' }, discoverable: { 'toot' => 'http://joinmastodon.org/ns#', 'discoverable' => 'toot:discoverable' }, voters_count: { 'toot' => 'http://joinmastodon.org/ns#', 'votersCount' => 'toot:votersCount' }, + olm: { 'toot' => 'http://joinmastodon.org/ns#', 'Device' => 'toot:Device', 'Ed25519Signature' => 'toot:Ed25519Signature', 'Ed25519Key' => 'toot:Ed25519Key', 'Curve25519Key' => 'toot:Curve25519Key', 'EncryptedMessage' => 'toot:EncryptedMessage', 'publicKeyBase64' => 'toot:publicKeyBase64', 'deviceId' => 'toot:deviceId', 'claim' => { '@type' => '@id', '@id' => 'toot:claim' }, 'fingerprintKey' => { '@type' => '@id', '@id' => 'toot:fingerprintKey' }, 'identityKey' => { '@type' => '@id', '@id' => 'toot:identityKey' }, 'devices' => { '@type' => '@id', '@id' => 'toot:devices' }, 'messageFranking' => 'toot:messageFranking', 'messageType' => 'toot:messageType', 'cipherText' => 'toot:cipherText' }, }.freeze def self.default_key_transform diff --git a/app/lib/inline_renderer.rb b/app/lib/inline_renderer.rb index 27e334a4d..b70814748 100644 --- a/app/lib/inline_renderer.rb +++ b/app/lib/inline_renderer.rb @@ -19,6 +19,8 @@ class InlineRenderer serializer = REST::AnnouncementSerializer when :reaction serializer = REST::ReactionSerializer + when :encrypted_message + serializer = REST::EncryptedMessageSerializer else return end -- cgit From c66403b2579587d8c1448be3ad30dc388d5f29c3 Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Mon, 8 Jun 2020 17:11:42 -0500 Subject: FIX: filters ignore media descriptions (#13837) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * FIX: filters ignore media descriptions * remove parentheses to make codeclimate happy * combine the text and run the regular expression only once. https://github.com/tootsuite/mastodon/pull/13837#discussion_r431752581 * Fix use of “filter” instead of “compact”, fix coding style issues Co-authored-by: Thibaut Girka --- app/javascript/mastodon/actions/importer/normalizer.js | 2 +- app/lib/feed_manager.rb | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'app/lib') diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js index f7cbe4c1c..dca44917a 100644 --- a/app/javascript/mastodon/actions/importer/normalizer.js +++ b/app/javascript/mastodon/actions/importer/normalizer.js @@ -12,7 +12,7 @@ const makeEmojiMap = record => record.emojis.reduce((obj, emoji) => { export function searchTextFromRawStatus (status) { const spoilerText = status.spoiler_text || ''; - const searchContent = ([spoilerText, status.content].concat((status.poll && status.poll.options) ? status.poll.options.map(option => option.title) : [])).join('\n\n').replace(//g, '\n').replace(/<\/p>

/g, '\n\n'); + const searchContent = ([spoilerText, status.content].concat((status.poll && status.poll.options) ? status.poll.options.map(option => option.title) : [])).concat(status.media_attachments.map(att => att.description)).join('\n\n').replace(//g, '\n').replace(/<\/p>

/g, '\n\n'); return domParser.parseFromString(searchContent, 'text/html').documentElement.textContent; } diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index 13b7aafdf..94872d050 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -246,9 +246,14 @@ class FeedManager combined_regex = active_filters.reduce { |memo, obj| Regexp.union(memo, obj) } status = status.reblog if status.reblog? - !combined_regex.match(Formatter.instance.plaintext(status)).nil? || - (status.spoiler_text.present? && !combined_regex.match(status.spoiler_text).nil?) || - (status.preloadable_poll && !combined_regex.match(status.preloadable_poll.options.join("\n\n")).nil?) + combined_text = [ + Formatter.instance.plaintext(status), + status.spoiler_text, + status.preloadable_poll ? status.preloadable_poll.options.join("\n\n") : nil, + status.media_attachments.map(&:description).join("\n\n"), + ].compact.join("\n\n") + + !combined_regex.match(combined_text).nil? end # Adds a status to an account's feed, returning true if a status was -- cgit