Merge branch 'master' into glitch-soc/merge-upstream
Conflicts:
- `.github/dependabot.yml`:
Updated upstream, we deleted it to not be flooded by Depandabot.
Kept deleted.
- `Gemfile.lock`:
Puma updated on both sides, went for the most recent version.
- `app/controllers/api/v1/mutes_controller.rb`:
Upstream updated the serializer to support timed mutes, while
glitch-soc added a custom API ages ago to get information that
is already available elsewhere.
Dropped the glitch-soc-specific API, went with upstream changes.
- `app/javascript/core/admin.js`:
Conflict due to changing how assets are loaded. Went with upstream.
- `app/javascript/packs/public.js`:
Conflict due to changing how assets are loaded. Went with upstream.
- `app/models/mute.rb`:
🤷
- `app/models/user.rb`:
New user setting added upstream while we have glitch-soc-specific
user settings. Added upstream's user setting.
- `config/settings.yml`:
Upstream added a new user setting close to a user setting we had
changed the defaults for. Added the new upstream setting.
- `package.json`:
Upstream dependency updated “too close” to a glitch-soc-specific
dependency. No real conflict. Updated the dependency.
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::FollowersSynchronizationsController, type: :controller do
|
||||
let!(:account) { Fabricate(:account) }
|
||||
let!(:follower_1) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/users/a') }
|
||||
let!(:follower_2) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/users/b') }
|
||||
let!(:follower_3) { Fabricate(:account, domain: 'foo.com', uri: 'https://foo.com/users/a') }
|
||||
|
||||
before do
|
||||
follower_1.follow!(account)
|
||||
follower_2.follow!(account)
|
||||
follower_3.follow!(account)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:signed_request_account).and_return(remote_account)
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
context 'without signature' do
|
||||
let(:remote_account) { nil }
|
||||
|
||||
before do
|
||||
get :show, params: { account_username: account.username }
|
||||
end
|
||||
|
||||
it 'returns http not authorized' do
|
||||
expect(response).to have_http_status(401)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with signature from example.com' do
|
||||
let(:remote_account) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/instance') }
|
||||
|
||||
before do
|
||||
get :show, params: { account_username: account.username }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns application/activity+json' do
|
||||
expect(response.content_type).to eq 'application/activity+json'
|
||||
end
|
||||
|
||||
it 'returns orderedItems with followers from example.com' do
|
||||
json = body_as_json
|
||||
expect(json[:orderedItems]).to be_an Array
|
||||
expect(json[:orderedItems].sort).to eq [follower_1.uri, follower_2.uri]
|
||||
end
|
||||
|
||||
it 'returns private Cache-Control header' do
|
||||
expect(response.headers['Cache-Control']).to eq 'max-age=0, private'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -22,6 +22,56 @@ RSpec.describe ActivityPub::InboxesController, type: :controller do
|
||||
end
|
||||
end
|
||||
|
||||
context 'with Collection-Synchronization header' do
|
||||
let(:remote_account) { Fabricate(:account, followers_url: 'https://example.com/followers', domain: 'example.com', uri: 'https://example.com/actor', protocol: :activitypub) }
|
||||
let(:synchronization_collection) { remote_account.followers_url }
|
||||
let(:synchronization_url) { 'https://example.com/followers-for-domain' }
|
||||
let(:synchronization_hash) { 'somehash' }
|
||||
let(:synchronization_header) { "collectionId=\"#{synchronization_collection}\", digest=\"#{synchronization_hash}\", url=\"#{synchronization_url}\"" }
|
||||
|
||||
before do
|
||||
allow(ActivityPub::FollowersSynchronizationWorker).to receive(:perform_async).and_return(nil)
|
||||
allow_any_instance_of(Account).to receive(:local_followers_hash).and_return('somehash')
|
||||
|
||||
request.headers['Collection-Synchronization'] = synchronization_header
|
||||
post :create, body: '{}'
|
||||
end
|
||||
|
||||
context 'with mismatching target collection' do
|
||||
let(:synchronization_collection) { 'https://example.com/followers2' }
|
||||
|
||||
it 'does not start a synchronization job' do
|
||||
expect(ActivityPub::FollowersSynchronizationWorker).not_to have_received(:perform_async)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with mismatching domain in partial collection attribute' do
|
||||
let(:synchronization_url) { 'https://example.org/followers' }
|
||||
|
||||
it 'does not start a synchronization job' do
|
||||
expect(ActivityPub::FollowersSynchronizationWorker).not_to have_received(:perform_async)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with matching digest' do
|
||||
it 'does not start a synchronization job' do
|
||||
expect(ActivityPub::FollowersSynchronizationWorker).not_to have_received(:perform_async)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with mismatching digest' do
|
||||
let(:synchronization_hash) { 'wronghash' }
|
||||
|
||||
it 'starts a synchronization job' do
|
||||
expect(ActivityPub::FollowersSynchronizationWorker).to have_received(:perform_async)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns http accepted' do
|
||||
expect(response).to have_http_status(202)
|
||||
end
|
||||
end
|
||||
|
||||
context 'without signature' do
|
||||
before do
|
||||
post :create, body: '{}'
|
||||
|
||||
@@ -43,8 +43,7 @@ describe RemoteFollowController do
|
||||
end
|
||||
|
||||
it 'renders new when template is nil' do
|
||||
link_with_nil_template = double(template: nil)
|
||||
resource_with_link = double(link: link_with_nil_template)
|
||||
resource_with_link = double(link: nil)
|
||||
allow_any_instance_of(WebfingerHelper).to receive(:webfinger!).with('acct:user@example.com').and_return(resource_with_link)
|
||||
post :create, params: { account_username: @account.to_param, remote_follow: { acct: 'user@example.com' } }
|
||||
|
||||
@@ -55,8 +54,7 @@ describe RemoteFollowController do
|
||||
|
||||
context 'when webfinger values are good' do
|
||||
before do
|
||||
link_with_template = double(template: 'http://example.com/follow_me?acct={uri}')
|
||||
resource_with_link = double(link: link_with_template)
|
||||
resource_with_link = double(link: 'http://example.com/follow_me?acct={uri}')
|
||||
allow_any_instance_of(WebfingerHelper).to receive(:webfinger!).with('acct:user@example.com').and_return(resource_with_link)
|
||||
post :create, params: { account_username: @account.to_param, remote_follow: { acct: 'user@example.com' } }
|
||||
end
|
||||
@@ -78,8 +76,8 @@ describe RemoteFollowController do
|
||||
expect(response).to render_template(:new)
|
||||
end
|
||||
|
||||
it 'renders new with error when goldfinger fails' do
|
||||
allow_any_instance_of(WebfingerHelper).to receive(:webfinger!).with('acct:user@example.com').and_raise(Goldfinger::Error)
|
||||
it 'renders new with error when webfinger fails' do
|
||||
allow_any_instance_of(WebfingerHelper).to receive(:webfinger!).with('acct:user@example.com').and_raise(Webfinger::Error)
|
||||
post :create, params: { account_username: @account.to_param, remote_follow: { acct: 'user@example.com' } }
|
||||
|
||||
expect(response).to render_template(:new)
|
||||
|
||||
@@ -12,7 +12,7 @@ describe WellKnown::HostMetaController, type: :controller do
|
||||
expect(response.body).to eq <<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
|
||||
<Link rel="lrdd" type="application/xrd+xml" template="https://cb6e6126.ngrok.io/.well-known/webfinger?resource={uri}"/>
|
||||
<Link rel="lrdd" template="https://cb6e6126.ngrok.io/.well-known/webfinger?resource={uri}"/>
|
||||
</XRD>
|
||||
XML
|
||||
end
|
||||
|
||||
6
spec/fabricators/ip_block_fabricator.rb
Normal file
6
spec/fabricators/ip_block_fabricator.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
Fabricator(:ip_block) do
|
||||
ip ""
|
||||
severity ""
|
||||
expires_at "2020-10-08 22:20:37"
|
||||
comment "MyText"
|
||||
end
|
||||
21
spec/lib/fast_ip_map_spec.rb
Normal file
21
spec/lib/fast_ip_map_spec.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe FastIpMap do
|
||||
describe '#include?' do
|
||||
subject { described_class.new([IPAddr.new('20.4.0.0/16'), IPAddr.new('145.22.30.0/24'), IPAddr.new('189.45.86.3')])}
|
||||
|
||||
it 'returns true for an exact match' do
|
||||
expect(subject.include?(IPAddr.new('189.45.86.3'))).to be true
|
||||
end
|
||||
|
||||
it 'returns true for a range match' do
|
||||
expect(subject.include?(IPAddr.new('20.4.45.7'))).to be true
|
||||
end
|
||||
|
||||
it 'returns false for no match' do
|
||||
expect(subject.include?(IPAddr.new('145.22.40.64'))).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -108,6 +108,7 @@ RSpec.describe FeedManager do
|
||||
|
||||
it 'returns false for status by followee mentioning another account' do
|
||||
bob.follow!(alice)
|
||||
jeff.follow!(alice)
|
||||
status = PostStatusService.new.call(alice, text: 'Hey @jeff')
|
||||
expect(FeedManager.instance.filter?(:home, status, bob)).to be false
|
||||
end
|
||||
|
||||
@@ -546,6 +546,49 @@ describe AccountInteractions do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#followers_hash' do
|
||||
let(:me) { Fabricate(:account, username: 'Me') }
|
||||
let(:remote_1) { Fabricate(:account, username: 'alice', domain: 'example.org', uri: 'https://example.org/users/alice') }
|
||||
let(:remote_2) { Fabricate(:account, username: 'bob', domain: 'example.org', uri: 'https://example.org/users/bob') }
|
||||
let(:remote_3) { Fabricate(:account, username: 'eve', domain: 'foo.org', uri: 'https://foo.org/users/eve') }
|
||||
|
||||
before do
|
||||
remote_1.follow!(me)
|
||||
remote_2.follow!(me)
|
||||
remote_3.follow!(me)
|
||||
me.follow!(remote_1)
|
||||
end
|
||||
|
||||
context 'on a local user' do
|
||||
it 'returns correct hash for remote domains' do
|
||||
expect(me.remote_followers_hash('https://example.org/')).to eq '707962e297b7bd94468a21bc8e506a1bcea607a9142cd64e27c9b106b2a5f6ec'
|
||||
expect(me.remote_followers_hash('https://foo.org/')).to eq 'ccb9c18a67134cfff9d62c7f7e7eb88e6b803446c244b84265565f4eba29df0e'
|
||||
end
|
||||
|
||||
it 'invalidates cache as needed when removing or adding followers' do
|
||||
expect(me.remote_followers_hash('https://example.org/')).to eq '707962e297b7bd94468a21bc8e506a1bcea607a9142cd64e27c9b106b2a5f6ec'
|
||||
remote_1.unfollow!(me)
|
||||
expect(me.remote_followers_hash('https://example.org/')).to eq '241b00794ce9b46aa864f3220afadef128318da2659782985bac5ed5bd436bff'
|
||||
remote_1.follow!(me)
|
||||
expect(me.remote_followers_hash('https://example.org/')).to eq '707962e297b7bd94468a21bc8e506a1bcea607a9142cd64e27c9b106b2a5f6ec'
|
||||
end
|
||||
end
|
||||
|
||||
context 'on a remote user' do
|
||||
it 'returns correct hash for remote domains' do
|
||||
expect(remote_1.local_followers_hash).to eq Digest::SHA256.hexdigest(ActivityPub::TagManager.instance.uri_for(me))
|
||||
end
|
||||
|
||||
it 'invalidates cache as needed when removing or adding followers' do
|
||||
expect(remote_1.local_followers_hash).to eq Digest::SHA256.hexdigest(ActivityPub::TagManager.instance.uri_for(me))
|
||||
me.unfollow!(remote_1)
|
||||
expect(remote_1.local_followers_hash).to eq '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
me.follow!(remote_1)
|
||||
expect(remote_1.local_followers_hash).to eq Digest::SHA256.hexdigest(ActivityPub::TagManager.instance.uri_for(me))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'muting an account' do
|
||||
let(:me) { Fabricate(:account, username: 'Me') }
|
||||
let(:you) { Fabricate(:account, username: 'You') }
|
||||
|
||||
5
spec/models/ip_block_spec.rb
Normal file
5
spec/models/ip_block_spec.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe IpBlock, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
||||
105
spec/services/activitypub/synchronize_followers_service_spec.rb
Normal file
105
spec/services/activitypub/synchronize_followers_service_spec.rb
Normal file
@@ -0,0 +1,105 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::SynchronizeFollowersService, type: :service do
|
||||
let(:actor) { Fabricate(:account, domain: 'example.com', uri: 'http://example.com/account', inbox_url: 'http://example.com/inbox') }
|
||||
let(:alice) { Fabricate(:account, username: 'alice') }
|
||||
let(:bob) { Fabricate(:account, username: 'bob') }
|
||||
let(:eve) { Fabricate(:account, username: 'eve') }
|
||||
let(:mallory) { Fabricate(:account, username: 'mallory') }
|
||||
let(:collection_uri) { 'http://example.com/partial-followers' }
|
||||
|
||||
let(:items) do
|
||||
[
|
||||
ActivityPub::TagManager.instance.uri_for(alice),
|
||||
ActivityPub::TagManager.instance.uri_for(eve),
|
||||
ActivityPub::TagManager.instance.uri_for(mallory),
|
||||
]
|
||||
end
|
||||
|
||||
let(:payload) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
type: 'Collection',
|
||||
id: collection_uri,
|
||||
items: items,
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
subject { described_class.new }
|
||||
|
||||
shared_examples 'synchronizes followers' do
|
||||
before do
|
||||
alice.follow!(actor)
|
||||
bob.follow!(actor)
|
||||
mallory.request_follow!(actor)
|
||||
|
||||
allow(ActivityPub::DeliveryWorker).to receive(:perform_async)
|
||||
|
||||
subject.call(actor, collection_uri)
|
||||
end
|
||||
|
||||
it 'keeps expected followers' do
|
||||
expect(alice.following?(actor)).to be true
|
||||
end
|
||||
|
||||
it 'removes local followers not in the remote list' do
|
||||
expect(bob.following?(actor)).to be false
|
||||
end
|
||||
|
||||
it 'converts follow requests to follow relationships when they have been accepted' do
|
||||
expect(mallory.following?(actor)).to be true
|
||||
end
|
||||
|
||||
it 'sends an Undo Follow to the actor' do
|
||||
expect(ActivityPub::DeliveryWorker).to have_received(:perform_async).with(anything, eve.id, actor.inbox_url)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#call' do
|
||||
context 'when the endpoint is a Collection of actor URIs' do
|
||||
before do
|
||||
stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload))
|
||||
end
|
||||
|
||||
it_behaves_like 'synchronizes followers'
|
||||
end
|
||||
|
||||
context 'when the endpoint is an OrderedCollection of actor URIs' do
|
||||
let(:payload) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
type: 'OrderedCollection',
|
||||
id: collection_uri,
|
||||
orderedItems: items,
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
before do
|
||||
stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload))
|
||||
end
|
||||
|
||||
it_behaves_like 'synchronizes followers'
|
||||
end
|
||||
|
||||
context 'when the endpoint is a paginated Collection of actor URIs' do
|
||||
let(:payload) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
type: 'Collection',
|
||||
id: collection_uri,
|
||||
first: {
|
||||
type: 'CollectionPage',
|
||||
partOf: collection_uri,
|
||||
items: items,
|
||||
}
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
before do
|
||||
stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload))
|
||||
end
|
||||
|
||||
it_behaves_like 'synchronizes followers'
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -3,6 +3,7 @@ require 'rails_helper'
|
||||
RSpec.describe AppSignUpService, type: :service do
|
||||
let(:app) { Fabricate(:application, scopes: 'read write') }
|
||||
let(:good_params) { { username: 'alice', password: '12345678', email: 'good@email.com', agreement: true } }
|
||||
let(:remote_ip) { IPAddr.new('198.0.2.1') }
|
||||
|
||||
subject { described_class.new }
|
||||
|
||||
@@ -10,16 +11,16 @@ RSpec.describe AppSignUpService, type: :service do
|
||||
it 'returns nil when registrations are closed' do
|
||||
tmp = Setting.registrations_mode
|
||||
Setting.registrations_mode = 'none'
|
||||
expect(subject.call(app, good_params)).to be_nil
|
||||
expect(subject.call(app, remote_ip, good_params)).to be_nil
|
||||
Setting.registrations_mode = tmp
|
||||
end
|
||||
|
||||
it 'raises an error when params are missing' do
|
||||
expect { subject.call(app, {}) }.to raise_error ActiveRecord::RecordInvalid
|
||||
expect { subject.call(app, remote_ip, {}) }.to raise_error ActiveRecord::RecordInvalid
|
||||
end
|
||||
|
||||
it 'creates an unconfirmed user with access token' do
|
||||
access_token = subject.call(app, good_params)
|
||||
access_token = subject.call(app, remote_ip, good_params)
|
||||
expect(access_token).to_not be_nil
|
||||
user = User.find_by(id: access_token.resource_owner_id)
|
||||
expect(user).to_not be_nil
|
||||
@@ -27,13 +28,13 @@ RSpec.describe AppSignUpService, type: :service do
|
||||
end
|
||||
|
||||
it 'creates access token with the app\'s scopes' do
|
||||
access_token = subject.call(app, good_params)
|
||||
access_token = subject.call(app, remote_ip, good_params)
|
||||
expect(access_token).to_not be_nil
|
||||
expect(access_token.scopes.to_s).to eq 'read write'
|
||||
end
|
||||
|
||||
it 'creates an account' do
|
||||
access_token = subject.call(app, good_params)
|
||||
access_token = subject.call(app, remote_ip, good_params)
|
||||
expect(access_token).to_not be_nil
|
||||
user = User.find_by(id: access_token.resource_owner_id)
|
||||
expect(user).to_not be_nil
|
||||
@@ -42,7 +43,7 @@ RSpec.describe AppSignUpService, type: :service do
|
||||
end
|
||||
|
||||
it 'creates an account with invite request text' do
|
||||
access_token = subject.call(app, good_params.merge(reason: 'Foo bar'))
|
||||
access_token = subject.call(app, remote_ip, good_params.merge(reason: 'Foo bar'))
|
||||
expect(access_token).to_not be_nil
|
||||
user = User.find_by(id: access_token.resource_owner_id)
|
||||
expect(user).to_not be_nil
|
||||
|
||||
@@ -3,16 +3,22 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe ActivityPub::DeliveryWorker do
|
||||
include RoutingHelper
|
||||
|
||||
subject { described_class.new }
|
||||
|
||||
let(:sender) { Fabricate(:account) }
|
||||
let(:payload) { 'test' }
|
||||
|
||||
before do
|
||||
allow_any_instance_of(Account).to receive(:remote_followers_hash).with('https://example.com/').and_return('somehash')
|
||||
end
|
||||
|
||||
describe 'perform' do
|
||||
it 'performs a request' do
|
||||
stub_request(:post, 'https://example.com/api').to_return(status: 200)
|
||||
subject.perform(payload, sender.id, 'https://example.com/api')
|
||||
expect(a_request(:post, 'https://example.com/api')).to have_been_made.once
|
||||
subject.perform(payload, sender.id, 'https://example.com/api', { synchronize_followers: true })
|
||||
expect(a_request(:post, 'https://example.com/api').with(headers: { 'Collection-Synchronization' => "collectionId=\"#{account_followers_url(sender)}\", digest=\"somehash\", url=\"#{account_followers_synchronization_url(sender)}\"" })).to have_been_made.once
|
||||
end
|
||||
|
||||
it 'raises when request fails' do
|
||||
|
||||
Reference in New Issue
Block a user