diff --git a/app/controllers/admin/friend_servers_controller.rb b/app/controllers/admin/friend_servers_controller.rb index aeec82429c..729d3b3912 100644 --- a/app/controllers/admin/friend_servers_controller.rb +++ b/app/controllers/admin/friend_servers_controller.rb @@ -35,7 +35,7 @@ module Admin def update authorize :friend_server, :update? - if @friend.update(resource_params) + if @friend.update(update_resource_params) redirect_to admin_friend_servers_path else render action: :edit @@ -79,7 +79,11 @@ module Admin end def resource_params - params.require(:friend_domain).permit(:domain, :inbox_url, :available, :pseudo_relay, :unlocked, :allow_all_posts) + params.require(:friend_domain).permit(:domain, :inbox_url, :available, :pseudo_relay, :delivery_local, :unlocked, :allow_all_posts) + end + + def update_resource_params + params.require(:friend_domain).permit(:inbox_url, :available, :pseudo_relay, :delivery_local, :unlocked, :allow_all_posts) end def warn_signatures_not_enabled! diff --git a/app/lib/activitypub/activity/accept.rb b/app/lib/activitypub/activity/accept.rb index 494400bffd..649c3503f7 100644 --- a/app/lib/activitypub/activity/accept.rb +++ b/app/lib/activitypub/activity/accept.rb @@ -45,7 +45,7 @@ class ActivityPub::Activity::Accept < ActivityPub::Activity end def accept_follow_for_friend - friend.update!(active_state: :accepted) + friend.update!(active_state: :accepted, passive_state: :idle) end def friend diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index be474a0519..b2bf2afc20 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -114,7 +114,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity end def process_status_params - @status_parser = ActivityPub::Parser::StatusParser.new(@json, followers_collection: @account.followers_url, object: @object, account: @account) + @status_parser = ActivityPub::Parser::StatusParser.new(@json, followers_collection: @account.followers_url, object: @object, account: @account, friend_domain: friend_domain?) @params = { uri: @status_parser.uri, @@ -506,6 +506,10 @@ class ActivityPub::Activity::Create < ActivityPub::Activity FriendDomain.free_receivings.exists?(domain: @account.domain) end + def friend_domain? + FriendDomain.enabled.find_by(domain: @account.domain)&.accepted? + end + def quote @quote ||= @object['quote'] || @object['quoteUrl'] || @object['quoteURL'] || @object['_misskey_quote'] end diff --git a/app/lib/activitypub/activity/follow.rb b/app/lib/activitypub/activity/follow.rb index d02e9c01c6..f5a0250163 100644 --- a/app/lib/activitypub/activity/follow.rb +++ b/app/lib/activitypub/activity/follow.rb @@ -49,16 +49,20 @@ class ActivityPub::Activity::Follow < ActivityPub::Activity already_accepted = false if friend.present? - already_accepted = friend.they_are_accepted? - friend.update!(passive_state: :pending, passive_follow_activity_id: @json['id']) + already_accepted = friend.accepted? + friend.update!(passive_state: :pending, active_state: :idle, passive_follow_activity_id: @json['id']) else - @friend = FriendDomain.create!(domain: @account.domain, passive_state: :pending, passive_follow_activity_id: @json['id']) + @friend = FriendDomain.new(domain: @account.domain, passive_state: :pending, passive_follow_activity_id: @json['id']) + @friend.initialize_inbox_url! + @friend.save! end - if already_accepted || friend.unlocked || Setting.unlocked_friend + if already_accepted || Setting.unlocked_friend friend.accept! - else + # Notify for admin even if unlocked + notify_staff_about_pending_friend_server! unless already_accepted + else notify_staff_about_pending_friend_server! end end diff --git a/app/lib/activitypub/activity/reject.rb b/app/lib/activitypub/activity/reject.rb index 0493400f86..e1eb3c2368 100644 --- a/app/lib/activitypub/activity/reject.rb +++ b/app/lib/activitypub/activity/reject.rb @@ -39,7 +39,7 @@ class ActivityPub::Activity::Reject < ActivityPub::Activity end def reject_follow_for_friend - friend.update!(active_state: :rejected) + friend.update!(active_state: :rejected, passive_state: :idle) end def friend diff --git a/app/lib/activitypub/activity/undo.rb b/app/lib/activitypub/activity/undo.rb index 2fc6bd2562..973143d8d2 100644 --- a/app/lib/activitypub/activity/undo.rb +++ b/app/lib/activitypub/activity/undo.rb @@ -103,7 +103,7 @@ class ActivityPub::Activity::Undo < ActivityPub::Activity end def remove_follow_from_friend - friend.update!(passive_state: :idle, passive_follow_activity_id: nil) + friend.destroy_without_signal! end def friend diff --git a/app/lib/activitypub/parser/status_parser.rb b/app/lib/activitypub/parser/status_parser.rb index d6d809a661..16c6c00424 100644 --- a/app/lib/activitypub/parser/status_parser.rb +++ b/app/lib/activitypub/parser/status_parser.rb @@ -11,6 +11,7 @@ class ActivityPub::Parser::StatusParser @object = magic_values[:object] || json['object'] || json @magic_values = magic_values @account = magic_values[:account] + @friend = magic_values[:friend_domain] end def uri @@ -76,7 +77,7 @@ class ActivityPub::Parser::StatusParser def visibility if audience_to.any? { |to| ActivityPub::TagManager.instance.public_collection?(to) } :public - elsif audience_to.include?('LocalPublic') + elsif audience_to.include?('LocalPublic') && @friend :public_unlisted elsif audience_cc.any? { |cc| ActivityPub::TagManager.instance.public_collection?(cc) } :unlisted @@ -200,7 +201,7 @@ class ActivityPub::Parser::StatusParser :public elsif audience_searchable_by.include?('kmyblue:Limited') || audience_searchable_by.include?('as:Limited') :limited - elsif audience_searchable_by.include?('LocalPublic') + elsif audience_searchable_by.include?('LocalPublic') && @friend :public_unlisted elsif audience_searchable_by.include?(@account.followers_url) :private diff --git a/app/lib/status_reach_finder.rb b/app/lib/status_reach_finder.rb index 169754e134..5a33418fab 100644 --- a/app/lib/status_reach_finder.rb +++ b/app/lib/status_reach_finder.rb @@ -10,7 +10,7 @@ class StatusReachFinder end def inboxes - (reached_account_inboxes + followers_inboxes + relay_inboxes).uniq + (reached_account_inboxes + followers_inboxes + relay_inboxes + nolocal_friend_inboxes).uniq end def inboxes_for_misskey @@ -147,7 +147,15 @@ class StatusReachFinder def friend_inboxes if @status.public_visibility? || @status.public_unlisted_visibility? || (@status.unlisted_visibility? && (@status.public_searchability? || @status.public_unlisted_searchability?)) - DeliveryFailureTracker.without_unavailable(FriendDomain.distributables.pluck(:inbox_url)) + DeliveryFailureTracker.without_unavailable(FriendDomain.distributables.where(delivery_local: true).pluck(:inbox_url)) + else + [] + end + end + + def nolocal_friend_inboxes + if @status.public_visibility? + DeliveryFailureTracker.without_unavailable(FriendDomain.distributables.where(delivery_local: false).pluck(:inbox_url)) else [] end diff --git a/app/models/friend_domain.rb b/app/models/friend_domain.rb index a3beb9b357..28cc59b334 100644 --- a/app/models/friend_domain.rb +++ b/app/models/friend_domain.rb @@ -13,10 +13,10 @@ # passive_follow_activity_id :string # available :boolean default(TRUE), not null # pseudo_relay :boolean default(FALSE), not null -# unlocked :boolean default(FALSE), not null # allow_all_posts :boolean default(TRUE), not null # created_at :datetime not null # updated_at :datetime not null +# delivery_local :boolean default(TRUE), not null # class FriendDomain < ApplicationRecord @@ -27,24 +27,31 @@ class FriendDomain < ApplicationRecord enum passive_state: { idle: 0, pending: 1, accepted: 2, rejected: 3 }, _prefix: :they_are scope :by_domain_and_subdomains, ->(domain) { where(domain: Instance.by_domain_and_subdomains(domain).select(:domain)) } - scope :enabled, -> { where(available: true) } - scope :mutuals, -> { enabled.where(active_state: :accepted, passive_state: :accepted) } - scope :distributables, -> { mutuals.where(pseudo_relay: true) } - scope :deliver_locals, -> { enabled.where(active_state: :accepted) } - scope :free_receivings, -> { mutuals.where(allow_all_posts: true) } + scope :enabled, -> { where(active_state: :accepted).or(FriendDomain.where(passive_state: :accepted)).where(available: true) } + scope :distributables, -> { enabled.where(pseudo_relay: true) } + scope :deliver_locals, -> { enabled.where(delivery_local: true) } + scope :free_receivings, -> { enabled.where(allow_all_posts: true) } before_destroy :ensure_disabled after_commit :set_default_inbox_url - def mutual? - i_am_accepted? && they_are_accepted? + def accepted? + i_am_accepted? || they_are_accepted? + end + + def pending? + !accepted? && (i_am_pending? || they_are_pending?) + end + + def idle? + (i_am_idle? || i_am_rejected?) && (they_are_idle? || they_are_rejected?) end def follow! activity_id = ActivityPub::TagManager.instance.generate_uri_for(nil) payload = Oj.dump(follow_activity(activity_id)) - update!(active_state: :pending, active_follow_activity_id: activity_id) + update!(active_state: :pending, passive_state: :idle, active_follow_activity_id: activity_id) DeliveryFailureTracker.reset!(inbox_url) ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url) end @@ -53,7 +60,7 @@ class FriendDomain < ApplicationRecord activity_id = ActivityPub::TagManager.instance.generate_uri_for(nil) payload = Oj.dump(unfollow_activity(activity_id)) - update!(active_state: :idle, active_follow_activity_id: nil) + update!(active_state: :idle, passive_state: :idle, active_follow_activity_id: nil) DeliveryFailureTracker.reset!(inbox_url) ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url) end @@ -64,7 +71,7 @@ class FriendDomain < ApplicationRecord activity_id = passive_follow_activity_id payload = Oj.dump(accept_follow_activity(activity_id)) - update!(passive_state: :accepted) + update!(passive_state: :accepted, active_state: :idle) DeliveryFailureTracker.reset!(inbox_url) ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url) end @@ -75,11 +82,21 @@ class FriendDomain < ApplicationRecord activity_id = passive_follow_activity_id payload = Oj.dump(reject_follow_activity(activity_id)) - update!(passive_state: :rejected, passive_follow_activity_id: nil) + update!(passive_state: :rejected, active_state: :idle, passive_follow_activity_id: nil) DeliveryFailureTracker.reset!(inbox_url) ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url) end + def destroy_without_signal! + self.active_state = :idle + self.passive_state = :idle + destroy! + end + + def initialize_inbox_url! + self.inbox_url = default_inbox_url + end + private def default_inbox_url @@ -154,7 +171,7 @@ class FriendDomain < ApplicationRecord end def ensure_disabled - delete_for_friend! unless i_am_idle? && they_are_idle? + delete_for_friend! unless id.nil? || (i_am_idle? && they_are_idle?) end def set_default_inbox_url diff --git a/app/views/admin/friend_servers/_friend_domain.html.haml b/app/views/admin/friend_servers/_friend_domain.html.haml index a24ae0516a..7da4db1b89 100644 --- a/app/views/admin/friend_servers/_friend_domain.html.haml +++ b/app/views/admin/friend_servers/_friend_domain.html.haml @@ -7,7 +7,7 @@ = t 'admin.friend_servers.disabled' %samp= friend.domain %td - - if friend.i_am_accepted? + - if friend.accepted? %span.positive-hint = fa_icon('check') = ' ' @@ -16,21 +16,11 @@ = fa_icon('hourglass') = ' ' = t 'admin.friend_servers.pending' - - else - %span.negative-hint - = fa_icon('times') - = ' ' - = t 'admin.friend_servers.disabled' - %td - - if friend.they_are_accepted? - %span.positive-hint - = fa_icon('check') - = ' ' - = t 'admin.friend_servers.enabled' - elsif friend.they_are_pending? - = fa_icon('hourglass') - = ' ' - = t 'admin.friend_servers.pending' + %span.warning-hint + = fa_icon('hourglass') + = ' ' + = t 'admin.friend_servers.pending_you' - else %span.negative-hint = fa_icon('times') diff --git a/app/views/admin/friend_servers/_friend_fields.html.haml b/app/views/admin/friend_servers/_friend_fields.html.haml index f2de4a1f60..ad9b0d60ca 100644 --- a/app/views/admin/friend_servers/_friend_fields.html.haml +++ b/app/views/admin/friend_servers/_friend_fields.html.haml @@ -10,11 +10,11 @@ .fields-group = f.input :available, as: :boolean, wrapper: :with_label, label: t('admin.friend_servers.edit.available') +.fields-group + = f.input :delivery_local, as: :boolean, wrapper: :with_label, label: t('admin.friend_servers.edit.delivery_local'), hint: t('admin.friend_servers.edit.delivery_local_hint') + .fields-group = f.input :pseudo_relay, as: :boolean, wrapper: :with_label, label: t('admin.friend_servers.edit.pseudo_relay'), hint: t('admin.friend_servers.edit.pseudo_relay_hint') -.fields-group - = f.input :unlocked, as: :boolean, wrapper: :with_label, label: t('admin.friend_servers.edit.unlocked') - .fields-group = f.input :allow_all_posts, as: :boolean, wrapper: :with_label, label: t('admin.friend_servers.edit.allow_all_posts'), hint: t('admin.friend_servers.edit.allow_all_posts_hint') diff --git a/app/views/admin/friend_servers/edit.html.haml b/app/views/admin/friend_servers/edit.html.haml index ae057c2963..06cff11bfb 100644 --- a/app/views/admin/friend_servers/edit.html.haml +++ b/app/views/admin/friend_servers/edit.html.haml @@ -6,35 +6,14 @@ = render 'friend_fields', f: f, friend: @friend .fields-group - %h4= t('admin.friend_servers.active_status') + %h4= t('admin.friend_servers.status') .fields-group - - if @friend.i_am_accepted? + - if @friend.accepted? %span.positive-hint = fa_icon('check') = ' ' = t 'admin.friend_servers.enabled' - - elsif @friend.i_am_pending? - = fa_icon('hourglass') - = ' ' - = t 'admin.friend_servers.pending' - - else - %span.negative-hint - = fa_icon('times') - = ' ' - = t 'admin.friend_servers.disabled' - .action-buttons - %div - = link_to t('admin.friend_servers.follow'), follow_admin_friend_server_path(@friend), class: 'button', method: :post, data: { confirm: t('admin.accounts.are_you_sure') } if @friend.i_am_idle? || @friend.i_am_rejected? - = link_to t('admin.friend_servers.unfollow'), unfollow_admin_friend_server_path(@friend), class: 'button', method: :post, data: { confirm: t('admin.accounts.are_you_sure') } if @friend.i_am_pending? || @friend.i_am_accepted? - - %h4= t('admin.friend_servers.passive_status') - .fields-gtoup - - if @friend.they_are_accepted? - %span.positive-hint - = fa_icon('check') - = ' ' - = t 'admin.friend_servers.enabled' - - elsif @friend.they_are_pending? + - elsif @friend.pending? = fa_icon('hourglass') = ' ' = t 'admin.friend_servers.pending' @@ -45,6 +24,7 @@ = t 'admin.friend_servers.disabled' .action-buttons %div + = link_to t('admin.friend_servers.follow'), follow_admin_friend_server_path(@friend), class: 'button', method: :post, data: { confirm: t('admin.accounts.are_you_sure') } if @friend.idle? = link_to t('admin.friend_servers.accept'), accept_admin_friend_server_path(@friend), class: 'button', method: :post, data: { confirm: t('admin.accounts.are_you_sure') } if @friend.they_are_pending? = link_to t('admin.friend_servers.reject'), reject_admin_friend_server_path(@friend), class: 'button', method: :post, data: { confirm: t('admin.accounts.are_you_sure') } if @friend.they_are_pending? diff --git a/app/views/admin/friend_servers/index.html.haml b/app/views/admin/friend_servers/index.html.haml index 6dd16e397e..196407c4c4 100644 --- a/app/views/admin/friend_servers/index.html.haml +++ b/app/views/admin/friend_servers/index.html.haml @@ -13,8 +13,7 @@ %thead %tr %th= t('admin.friend_servers.domain') - %th= t('admin.friend_servers.active_status') - %th= t('admin.friend_servers.passive_status') + %th= t('admin.friend_servers.status') %th %tbody - @friends.each do |friend| diff --git a/config/locales/en.yml b/config/locales/en.yml index 2185f933cb..23b3bf24e2 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -501,7 +501,6 @@ en: unsuppress: Restore follow recommendation friend_servers: accept: Accept - active_status: My status add_new: Add and make a new application delete: Delete description_html: フレンドサーバーとは、お互いのローカル公開・ローカル検索許可の投稿をそのまま交換するシステムです。 @@ -511,6 +510,8 @@ en: allow_all_posts: Receive all posts allow_all_posts_hint: 通常は自分のサーバーの誰もフォローしていないアカウントの投稿は例外を除き受け入れがブロックされます。そのブロックを解除します。スパムが発生した場合など、いつでもブロックを再開できます。 available: Available + delivery_local: Deliver without changing public unlisted visibility and searchability + delivery_local_hint: Public unlisted posts will be added the friend's global timeline description: フレンドサーバーは、登録と同時に相手方のサーバーへ申請されます。 domain: Domain inbox_url: Friend server inbox URL @@ -521,8 +522,8 @@ en: edit_friend: Edit enabled: Enabled follow: Request - passive_status: Partner status pending: Pending + pending_you: Your review requested reject: Reject save_and_enable: Save and enable setup: Add and make a new application diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 61189de940..2c24017c36 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -496,7 +496,6 @@ ja: unsuppress: おすすめフォローを復元 friend_servers: accept: 相手の申請を承認する - active_status: 自分の状態 add_new: フレンドサーバーを追加・申請 delete: 削除 description_html: フレンドサーバーとは、お互いのローカル公開・ローカル検索許可の投稿をそのまま交換するシステムです。 @@ -506,6 +505,8 @@ ja: allow_all_posts: このサーバーからの投稿を無条件で受け入れる allow_all_posts_hint: 通常は自分のサーバーの誰もフォローしていないアカウントの投稿は例外を除き受け入れがブロックされます。そのブロックを解除します。スパムが発生した場合など、いつでもブロックを再開できます。 available: 有効にする + delivery_local: ローカル公開の公開範囲・検索許可を持った投稿をそのまま相手と共有する + delivery_local_hint: ローカル公開投稿は、通常は非収載に変換されて配送されます。その処理をせず、相手サーバーにもローカル公開と認識されるようにします。相手の連合タイムラインに掲載されます description: フレンドサーバーは、登録と同時に相手方のサーバーへ申請されます。 domain: ドメイン inbox_url: フレンドサーバーの inbox URL @@ -516,8 +517,8 @@ ja: edit_friend: 編集 enabled: 有効 follow: こちらから申請する - passive_status: 相手の状態 pending: 承認待ち + pending_you: あなたの承認が必要 reject: 相手からの申請を却下する save_and_enable: 保存して有効にする setup: フレンドサーバーを追加・申請 diff --git a/db/migrate/20231009235215_add_delivery_local_to_friend_domains.rb b/db/migrate/20231009235215_add_delivery_local_to_friend_domains.rb new file mode 100644 index 0000000000..a42b016663 --- /dev/null +++ b/db/migrate/20231009235215_add_delivery_local_to_friend_domains.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require Rails.root.join('lib', 'mastodon', 'migration_helpers') + +class AddDeliveryLocalToFriendDomains < ActiveRecord::Migration[7.0] + include Mastodon::MigrationHelpers + + disable_ddl_transaction! + + def up + safety_assured do + add_column_with_default :friend_domains, :delivery_local, :boolean, default: true, allow_null: false + remove_column :friend_domains, :unlocked + end + end + + def down + safety_assured do + remove_column :friend_domains, :delivery_local + add_column_with_default :friend_domains, :unlocked, :boolean, default: false, allow_null: false + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 4a1401efbc..7a4ee11608 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_10_07_090808) do +ActiveRecord::Schema[7.0].define(version: 2023_10_09_235215) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -686,10 +686,10 @@ ActiveRecord::Schema[7.0].define(version: 2023_10_07_090808) do t.string "passive_follow_activity_id" t.boolean "available", default: true, null: false t.boolean "pseudo_relay", default: false, null: false - t.boolean "unlocked", default: false, null: false t.boolean "allow_all_posts", default: true, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.boolean "delivery_local", default: true, null: false t.index ["domain"], name: "index_friend_domains_on_domain", unique: true t.index ["inbox_url"], name: "index_friend_domains_on_inbox_url", unique: true end diff --git a/spec/lib/activitypub/activity/accept_spec.rb b/spec/lib/activitypub/activity/accept_spec.rb index 6027557092..24dbcbff7c 100644 --- a/spec/lib/activitypub/activity/accept_spec.rb +++ b/spec/lib/activitypub/activity/accept_spec.rb @@ -118,5 +118,26 @@ RSpec.describe ActivityPub::Activity::Accept do subject.perform expect(friend.reload.i_am_accepted?).to be true end + + it 'when the friend server is pending' do + friend.update(passive_state: :pending) + subject.perform + expect(friend.reload.they_are_idle?).to be true + expect(friend.i_am_accepted?).to be true + end + + it 'when the friend server is accepted' do + friend.update(passive_state: :accepted) + subject.perform + expect(friend.reload.they_are_idle?).to be true + expect(friend.i_am_accepted?).to be true + end + + it 'when my server is not pending' do + friend.update(active_state: :idle) + subject.perform + expect(friend.reload.i_am_idle?).to be true + expect(friend.they_are_idle?).to be true + end end end diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb index 4651445ae6..93b953fa34 100644 --- a/spec/lib/activitypub/activity/create_spec.rb +++ b/spec/lib/activitypub/activity/create_spec.rb @@ -30,9 +30,11 @@ RSpec.describe ActivityPub::Activity::Create do let(:sender_software) { 'mastodon' } let(:custom_before) { false } + let(:active_friend) { false } before do Fabricate(:instance_info, domain: 'example.com', software: sender_software) + Fabricate(:friend_domain, domain: 'example.com', active_state: :accepted) if active_friend subject.perform unless custom_before end @@ -245,6 +247,26 @@ RSpec.describe ActivityPub::Activity::Create do } end + it 'creates status' do + status = sender.statuses.first + + expect(status).to_not be_nil + expect(status.visibility).to eq 'unlisted' + end + end + + context 'when public_unlisted with LocalPublic from friend-server' do + let(:object_json) do + { + id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, + type: 'Note', + content: 'Lorem ipsum', + to: ['http://example.com/followers', 'LocalPublic'], + cc: 'https://www.w3.org/ns/activitystreams#Public', + } + end + let(:active_friend) { true } + it 'creates status' do status = sender.statuses.first @@ -433,6 +455,18 @@ RSpec.describe ActivityPub::Activity::Create do context 'with public_unlisted with LocalPublic' do let(:searchable_by) { ['http://example.com/followers', 'LocalPublic'] } + it 'create status' do + status = sender.statuses.first + + expect(status).to_not be_nil + expect(status.searchability).to eq 'private' + end + end + + context 'with public_unlisted with LocalPublic from friend-server' do + let(:searchable_by) { ['http://example.com/followers', 'LocalPublic'] } + let(:active_friend) { true } + it 'create status' do status = sender.statuses.first @@ -1506,11 +1540,7 @@ RSpec.describe ActivityPub::Activity::Create do context 'when sender is in friend server' do subject { described_class.new(json, sender, delivery: true) } - before do - Fabricate(:friend_domain, domain: sender.domain, active_state: :accepted, passive_state: :accepted) - subject.perform - end - + let!(:friend) { Fabricate(:friend_domain, domain: sender.domain, active_state: :accepted) } let(:object_json) do { id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, @@ -1520,11 +1550,20 @@ RSpec.describe ActivityPub::Activity::Create do end it 'creates status' do + subject.perform status = sender.statuses.first expect(status).to_not be_nil expect(status.text).to eq 'Lorem ipsum' end + + it 'whey no-relay not creates status' do + friend.update(allow_all_posts: false) + subject.perform + status = sender.statuses.first + + expect(status).to be_nil + end end context 'when the sender has no relevance to local activity' do diff --git a/spec/lib/activitypub/activity/follow_spec.rb b/spec/lib/activitypub/activity/follow_spec.rb index a69fa9b563..c27696ebaf 100644 --- a/spec/lib/activitypub/activity/follow_spec.rb +++ b/spec/lib/activitypub/activity/follow_spec.rb @@ -339,6 +339,37 @@ RSpec.describe ActivityPub::Activity::Follow do expect(friend).to_not be_nil expect(friend.they_are_pending?).to be true expect(friend.passive_follow_activity_id).to eq 'foo' + expect(friend.inbox_url).to eq 'https://abc.com/inbox' + end + end + + context 'when my server is pending' do + before do + friend.update(active_state: :pending) + end + + it 'marks me as idle' do + subject.perform + expect(friend.reload.they_are_pending?).to be true + expect(friend.i_am_idle?).to be true + end + end + + context 'when my server is already accepted' do + before do + friend.update(active_state: :accepted) + stub_request(:post, 'https://example.com/inbox') + end + + it 'marks me as idle and the friend as accepted' do + subject.perform + expect(friend.reload.they_are_accepted?).to be true + expect(friend.i_am_idle?).to be true + expect(a_request(:post, 'https://example.com/inbox').with(body: hash_including({ + id: 'foo#accepts/friends', + type: 'Accept', + object: 'foo', + }))).to have_been_made.once end end @@ -372,26 +403,6 @@ RSpec.describe ActivityPub::Activity::Follow do end end - context 'when unlocked' do - before do - friend.update(unlocked: true) - stub_request(:post, 'https://example.com/inbox') - end - - it 'marks the friend as accepted' do - subject.perform - - friend = FriendDomain.find_by(domain: 'abc.com') - expect(friend).to_not be_nil - expect(friend.they_are_accepted?).to be true - expect(a_request(:post, 'https://example.com/inbox').with(body: hash_including({ - id: 'foo#accepts/friends', - type: 'Accept', - object: 'foo', - }))).to have_been_made.once - end - end - context 'when unlocked on admin settings' do before do Form::AdminSettings.new(unlocked_friend: '1').save diff --git a/spec/lib/activitypub/activity/reject_spec.rb b/spec/lib/activitypub/activity/reject_spec.rb index 1eee37db35..9ebbded42e 100644 --- a/spec/lib/activitypub/activity/reject_spec.rb +++ b/spec/lib/activitypub/activity/reject_spec.rb @@ -192,5 +192,19 @@ RSpec.describe ActivityPub::Activity::Reject do subject.perform expect(friend.reload.i_am_rejected?).to be true end + + it 'when the friend server is pending' do + friend.update(passive_state: :pending) + subject.perform + expect(friend.reload.they_are_idle?).to be true + expect(friend.i_am_rejected?).to be true + end + + it 'when the friend server is accepted' do + friend.update(passive_state: :accepted) + subject.perform + expect(friend.reload.they_are_idle?).to be true + expect(friend.i_am_rejected?).to be true + end end end diff --git a/spec/lib/activitypub/activity/undo_spec.rb b/spec/lib/activitypub/activity/undo_spec.rb index 1671f04b4b..4634cb967e 100644 --- a/spec/lib/activitypub/activity/undo_spec.rb +++ b/spec/lib/activitypub/activity/undo_spec.rb @@ -149,7 +149,7 @@ RSpec.describe ActivityPub::Activity::Undo do friend = Fabricate(:friend_domain, domain: sender.domain, passive_state: :accepted) subject.perform expect(sender.following?(recipient)).to be false - expect(friend.they_are_accepted?).to be true + expect(friend.reload.they_are_accepted?).to be true end context 'with only object uri' do @@ -175,8 +175,19 @@ RSpec.describe ActivityPub::Activity::Undo do it 'deletes follow from this server to friend' do subject.perform - expect(friend.reload.they_are_idle?).to be true - expect(friend.passive_follow_activity_id).to be_nil + expect(FriendDomain.exists?(domain: 'abc.com')).to be false + end + + it 'when my server is pending' do + friend.update(active_state: :pending) + subject.perform + expect(FriendDomain.exists?(domain: 'abc.com')).to be false + end + + it 'when my server is accepted' do + friend.update(active_state: :accepted) + subject.perform + expect(FriendDomain.exists?(domain: 'abc.com')).to be false end end end diff --git a/spec/lib/status_reach_finder_spec.rb b/spec/lib/status_reach_finder_spec.rb index 8aefc85210..2d8e075d5b 100644 --- a/spec/lib/status_reach_finder_spec.rb +++ b/spec/lib/status_reach_finder_spec.rb @@ -99,7 +99,7 @@ describe StatusReachFinder do let(:sender_software) { 'misskey' } let(:searchability) { :public } - before { Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', available: true, active_state: :accepted, passive_state: :accepted, pseudo_relay: true) } + before { Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', available: true, active_state: :accepted, pseudo_relay: true) } it 'send status without friend server' do expect(subject.inboxes).to_not include 'https://foo.bar/inbox' @@ -114,7 +114,7 @@ describe StatusReachFinder do context 'with follower' do before do - Fabricate(:friend_domain, domain: 'foo.bar', active_state: :accepted) + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :accepted) bob.follow!(alice) end @@ -124,9 +124,21 @@ describe StatusReachFinder do end end - context 'with non-follower' do + context 'with follower but not local-distributable' do before do - Fabricate(:friend_domain, domain: 'foo.bar', active_state: :accepted) + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :accepted, delivery_local: false) + bob.follow!(alice) + end + + it 'send status' do + expect(subject.inboxes).to include 'https://foo.bar/inbox' + expect(subject.inboxes_for_friend).to_not include 'https://foo.bar/inbox' + end + end + + context 'with non-follower and non-relay' do + before do + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :accepted) end it 'send status' do @@ -137,7 +149,7 @@ describe StatusReachFinder do context 'with pending' do before do - Fabricate(:friend_domain, domain: 'foo.bar', active_state: :pending) + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :pending) bob.follow!(alice) end @@ -147,21 +159,21 @@ describe StatusReachFinder do end end - context 'with idle' do + context 'with unidirection from them' do before do - Fabricate(:friend_domain, domain: 'foo.bar', active_state: :idle, passive_state: :accepted) + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :idle, passive_state: :accepted) bob.follow!(alice) end it 'send status' do - expect(subject.inboxes).to include 'https://foo.bar/inbox' - expect(subject.inboxes_for_friend).to_not include 'https://foo.bar/inbox' + expect(subject.inboxes).to_not include 'https://foo.bar/inbox' + expect(subject.inboxes_for_friend).to include 'https://foo.bar/inbox' end end context 'when unavailable' do before do - Fabricate(:friend_domain, domain: 'foo.bar', active_state: :accepted, available: false) + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :accepted, available: false) bob.follow!(alice) end @@ -173,7 +185,18 @@ describe StatusReachFinder do context 'when distributable' do before do - Fabricate(:friend_domain, domain: 'foo.bar', active_state: :accepted, passive_state: :accepted, pseudo_relay: true) + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', passive_state: :accepted, pseudo_relay: true) + end + + it 'send status' do + expect(subject.inboxes).to_not include 'https://foo.bar/inbox' + expect(subject.inboxes_for_friend).to include 'https://foo.bar/inbox' + end + end + + context 'when distributable and following' do + before do + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', passive_state: :accepted, pseudo_relay: true) bob.follow!(alice) end @@ -183,9 +206,9 @@ describe StatusReachFinder do end end - context 'when distributable and not following' do + context 'when distributable reverse' do before do - Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :accepted, passive_state: :accepted, pseudo_relay: true) + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', active_state: :accepted, pseudo_relay: true) end it 'send status' do @@ -193,10 +216,35 @@ describe StatusReachFinder do expect(subject.inboxes_for_friend).to include 'https://foo.bar/inbox' end end + + context 'when distributable but not local distributable' do + before do + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', passive_state: :accepted, pseudo_relay: true, delivery_local: false) + end + + it 'send status' do + expect(subject.inboxes).to include 'https://foo.bar/inbox' + expect(subject.inboxes_for_friend).to_not include 'https://foo.bar/inbox' + end + end + + context 'when distributable and following but not local distributable' do + before do + Fabricate(:friend_domain, domain: 'foo.bar', passive_state: :accepted, pseudo_relay: true, delivery_local: false) + bob.follow!(alice) + end + + it 'send status' do + expect(subject.inboxes).to include 'https://foo.bar/inbox' + expect(subject.inboxes_for_friend).to_not include 'https://foo.bar/inbox' + end + end end context 'when it contains distributable friend server' do - before { Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', available: true, active_state: :accepted, passive_state: :accepted, pseudo_relay: true) } + before do + Fabricate(:friend_domain, domain: 'foo.bar', inbox_url: 'https://foo.bar/inbox', passive_state: :accepted, pseudo_relay: true) + end it 'includes the inbox of the mentioned account' do expect(subject.inboxes).to_not include 'https://foo.bar/inbox' @@ -381,9 +429,11 @@ describe StatusReachFinder do Fabricate(:friend_domain, domain: 'def.com', inbox_url: 'https://def.com/inbox', active_state: :accepted, passive_state: :accepted, pseudo_relay: true, available: true) Fabricate(:friend_domain, domain: 'ghi.com', inbox_url: 'https://ghi.com/inbox', active_state: :accepted, passive_state: :accepted, pseudo_relay: true, available: false) Fabricate(:friend_domain, domain: 'jkl.com', inbox_url: 'https://jkl.com/inbox', active_state: :accepted, passive_state: :accepted, pseudo_relay: false, available: true) - Fabricate(:friend_domain, domain: 'mno.com', inbox_url: 'https://mno.com/inbox', active_state: :accepted, passive_state: :pending, pseudo_relay: true, available: true) + Fabricate(:friend_domain, domain: 'mno.com', inbox_url: 'https://mno.com/inbox', active_state: :accepted, passive_state: :idle, pseudo_relay: true, available: true) Fabricate(:friend_domain, domain: 'pqr.com', inbox_url: 'https://pqr.com/inbox', active_state: :accepted, passive_state: :accepted, pseudo_relay: true, available: true) Fabricate(:unavailable_domain, domain: 'pqr.com') + Fabricate(:friend_domain, domain: 'stu.com', inbox_url: 'https://stu.com/inbox', active_state: :idle, passive_state: :accepted, pseudo_relay: true, available: true) + Fabricate(:friend_domain, domain: 'vwx.com', inbox_url: 'https://vwx.com/inbox', active_state: :idle, passive_state: :accepted, pseudo_relay: true, available: true, delivery_local: false) end it 'returns friend servers' do @@ -399,8 +449,13 @@ describe StatusReachFinder do expect(subject).to_not include 'https://jkl.com/inbox' end - it 'not contains no-mutual friends' do - expect(subject).to_not include 'https://mno.com/inbox' + it 'contains no-mutual friends' do + expect(subject).to include 'https://mno.com/inbox' + expect(subject).to include 'https://stu.com/inbox' + end + + it 'not contains un local distable' do + expect(subject).to_not include 'https://vwx.com/inbox' end it 'not contains unavailable domain friends' do diff --git a/spec/models/friend_domain_spec.rb b/spec/models/friend_domain_spec.rb index c3fa128b16..647c39e5a8 100644 --- a/spec/models/friend_domain_spec.rb +++ b/spec/models/friend_domain_spec.rb @@ -11,9 +11,11 @@ describe FriendDomain do describe '#follow!' do it 'call inbox' do + friend.update(active_state: :accepted, passive_state: :accepted) friend.follow! expect(friend.active_follow_activity_id).to_not be_nil expect(friend.i_am_pending?).to be true + expect(friend.they_are_idle?).to be true expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({ id: friend.active_follow_activity_id, type: 'Follow', @@ -25,10 +27,11 @@ describe FriendDomain do describe '#unfollow!' do it 'call inbox' do - friend.update(active_follow_activity_id: 'ohagi') + friend.update(active_follow_activity_id: 'ohagi', active_state: :accepted, passive_state: :accepted) friend.unfollow! expect(friend.active_follow_activity_id).to be_nil expect(friend.i_am_idle?).to be true + expect(friend.they_are_idle?).to be true expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({ type: 'Undo', object: { @@ -43,9 +46,10 @@ describe FriendDomain do describe '#accept!' do it 'call inbox' do - friend.update(passive_follow_activity_id: 'ohagi', passive_state: :pending) + friend.update(passive_follow_activity_id: 'ohagi', active_state: :accepted, passive_state: :pending) friend.accept! expect(friend.they_are_accepted?).to be true + expect(friend.i_am_idle?).to be true expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({ id: 'ohagi#accepts/friends', type: 'Accept', @@ -57,9 +61,10 @@ describe FriendDomain do describe '#reject!' do it 'call inbox' do - friend.update(passive_follow_activity_id: 'ohagi', passive_state: :pending) + friend.update(passive_follow_activity_id: 'ohagi', active_state: :accepted, passive_state: :pending) friend.reject! expect(friend.they_are_rejected?).to be true + expect(friend.i_am_idle?).to be true expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({ id: 'ohagi#rejects/friends', type: 'Reject',