Add support for reversible suspensions through ActivityPub (#14989)
This commit is contained in:
@ -16,17 +16,49 @@ describe AccountFollowController do
|
||||
allow(service).to receive(:call)
|
||||
end
|
||||
|
||||
it 'does not create for user who is not signed in' do
|
||||
subject
|
||||
expect(FollowService).not_to receive(:new)
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
alice.suspend!
|
||||
alice.deletion_request.destroy
|
||||
subject
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
it 'redirects to account path' do
|
||||
sign_in(user)
|
||||
subject
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
alice.suspend!
|
||||
subject
|
||||
end
|
||||
|
||||
expect(service).to have_received(:call).with(user.account, alice, with_rate_limit: true)
|
||||
expect(response).to redirect_to(account_path(alice))
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when signed out' do
|
||||
before do
|
||||
subject
|
||||
end
|
||||
|
||||
it 'does not follow' do
|
||||
expect(FollowService).not_to receive(:new)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when signed in' do
|
||||
before do
|
||||
sign_in(user)
|
||||
subject
|
||||
end
|
||||
|
||||
it 'redirects to account path' do
|
||||
expect(service).to have_received(:call).with(user.account, alice, with_rate_limit: true)
|
||||
expect(response).to redirect_to(account_path(alice))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -16,17 +16,49 @@ describe AccountUnfollowController do
|
||||
allow(service).to receive(:call)
|
||||
end
|
||||
|
||||
it 'does not create for user who is not signed in' do
|
||||
subject
|
||||
expect(UnfollowService).not_to receive(:new)
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
alice.suspend!
|
||||
alice.deletion_request.destroy
|
||||
subject
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
it 'redirects to account path' do
|
||||
sign_in(user)
|
||||
subject
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
alice.suspend!
|
||||
subject
|
||||
end
|
||||
|
||||
expect(service).to have_received(:call).with(user.account, alice)
|
||||
expect(response).to redirect_to(account_path(alice))
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when signed out' do
|
||||
before do
|
||||
subject
|
||||
end
|
||||
|
||||
it 'does not unfollow' do
|
||||
expect(UnfollowService).not_to receive(:new)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when signed in' do
|
||||
before do
|
||||
sign_in(user)
|
||||
subject
|
||||
end
|
||||
|
||||
it 'redirects to account path' do
|
||||
expect(service).to have_received(:call).with(user.account, alice)
|
||||
expect(response).to redirect_to(account_path(alice))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -48,10 +48,17 @@ RSpec.describe AccountsController, type: :controller do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is suspended' do
|
||||
context 'as HTML' do
|
||||
let(:format) { 'html' }
|
||||
|
||||
it_behaves_like 'preliminary checks'
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
account.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
@ -59,12 +66,17 @@ RSpec.describe AccountsController, type: :controller do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'as HTML' do
|
||||
let(:format) { 'html' }
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
end
|
||||
|
||||
it_behaves_like 'preliminary checks'
|
||||
it 'returns http forbidden' do
|
||||
get :show, params: { username: account.username, format: format }
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'common response characteristics' do
|
||||
it 'returns http success' do
|
||||
@ -325,6 +337,29 @@ RSpec.describe AccountsController, type: :controller do
|
||||
|
||||
it_behaves_like 'preliminary checks'
|
||||
|
||||
context 'when account is suspended permanently' do
|
||||
before do
|
||||
account.suspend!
|
||||
account.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
get :show, params: { username: account.username, format: format }
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is suspended temporarily' do
|
||||
before do
|
||||
account.suspend!
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
get :show, params: { username: account.username, format: format }
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
context do
|
||||
before do
|
||||
get :show, params: { username: account.username, format: format }
|
||||
@ -435,6 +470,29 @@ RSpec.describe AccountsController, type: :controller do
|
||||
|
||||
it_behaves_like 'preliminary checks'
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
account.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
get :show, params: { username: account.username, format: format }
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
end
|
||||
|
||||
it 'returns http forbidden' do
|
||||
get :show, params: { username: account.username, format: format }
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'common response characteristics' do
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
|
@ -13,6 +13,7 @@ RSpec.describe ActivityPub::CollectionsController, type: :controller do
|
||||
end
|
||||
|
||||
it 'does not set sessions' do
|
||||
response
|
||||
expect(session).to be_empty
|
||||
end
|
||||
|
||||
@ -34,9 +35,8 @@ RSpec.describe ActivityPub::CollectionsController, type: :controller do
|
||||
context 'without signature' do
|
||||
let(:remote_account) { nil }
|
||||
|
||||
before do
|
||||
get :show, params: { id: 'featured', account_username: account.username }
|
||||
end
|
||||
subject(:response) { get :show, params: { id: 'featured', account_username: account.username } }
|
||||
subject(:body) { body_as_json }
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
@ -49,9 +49,29 @@ RSpec.describe ActivityPub::CollectionsController, type: :controller do
|
||||
it_behaves_like 'cachable response'
|
||||
|
||||
it 'returns orderedItems with pinned statuses' do
|
||||
json = body_as_json
|
||||
expect(json[:orderedItems]).to be_an Array
|
||||
expect(json[:orderedItems].size).to eq 2
|
||||
expect(body[:orderedItems]).to be_an Array
|
||||
expect(body[:orderedItems].size).to eq 2
|
||||
end
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
account.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
end
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -32,9 +32,8 @@ RSpec.describe ActivityPub::FollowersSynchronizationsController, type: :controll
|
||||
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
|
||||
subject(:response) { get :show, params: { account_username: account.username } }
|
||||
subject(:body) { body_as_json }
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
@ -45,14 +44,34 @@ RSpec.describe ActivityPub::FollowersSynchronizationsController, type: :controll
|
||||
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]
|
||||
expect(body[:orderedItems]).to be_an Array
|
||||
expect(body[: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
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
account.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
end
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -20,6 +20,33 @@ RSpec.describe ActivityPub::InboxesController, type: :controller do
|
||||
it 'returns http accepted' do
|
||||
expect(response).to have_http_status(202)
|
||||
end
|
||||
|
||||
context 'for a specific account' do
|
||||
let(:account) { Fabricate(:account) }
|
||||
|
||||
subject(:response) { post :create, params: { account_username: account.username }, body: '{}' }
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
account.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
end
|
||||
|
||||
it 'returns http accepted' do
|
||||
expect(response).to have_http_status(202)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with Collection-Synchronization header' do
|
||||
|
@ -10,6 +10,7 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do
|
||||
end
|
||||
|
||||
it 'does not set sessions' do
|
||||
response
|
||||
expect(session).to be_empty
|
||||
end
|
||||
|
||||
@ -34,9 +35,8 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do
|
||||
context 'without signature' do
|
||||
let(:remote_account) { nil }
|
||||
|
||||
before do
|
||||
get :show, params: { account_username: account.username, page: page }
|
||||
end
|
||||
subject(:response) { get :show, params: { account_username: account.username, page: page } }
|
||||
subject(:body) { body_as_json }
|
||||
|
||||
context 'with page not requested' do
|
||||
let(:page) { nil }
|
||||
@ -50,11 +50,31 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do
|
||||
end
|
||||
|
||||
it 'returns totalItems' do
|
||||
json = body_as_json
|
||||
expect(json[:totalItems]).to eq 4
|
||||
expect(body[:totalItems]).to eq 4
|
||||
end
|
||||
|
||||
it_behaves_like 'cachable response'
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
account.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
end
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with page requested' do
|
||||
@ -69,13 +89,33 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do
|
||||
end
|
||||
|
||||
it 'returns orderedItems with public or unlisted statuses' do
|
||||
json = body_as_json
|
||||
expect(json[:orderedItems]).to be_an Array
|
||||
expect(json[:orderedItems].size).to eq 2
|
||||
expect(json[:orderedItems].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) }).to be true
|
||||
expect(body[:orderedItems]).to be_an Array
|
||||
expect(body[:orderedItems].size).to eq 2
|
||||
expect(body[:orderedItems].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) }).to be true
|
||||
end
|
||||
|
||||
it_behaves_like 'cachable response'
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
account.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
end
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -14,6 +14,7 @@ RSpec.describe ActivityPub::RepliesController, type: :controller do
|
||||
end
|
||||
|
||||
it 'does not set sessions' do
|
||||
response
|
||||
expect(session).to be_empty
|
||||
end
|
||||
|
||||
@ -36,8 +37,32 @@ RSpec.describe ActivityPub::RepliesController, type: :controller do
|
||||
|
||||
describe 'GET #index' do
|
||||
context 'with no signature' do
|
||||
before do
|
||||
get :index, params: { account_username: status.account.username, status_id: status.id }
|
||||
subject(:response) { get :index, params: { account_username: status.account.username, status_id: status.id } }
|
||||
subject(:body) { body_as_json }
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
let(:parent_visibility) { :public }
|
||||
|
||||
before do
|
||||
status.account.suspend!
|
||||
status.account.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
let(:parent_visibility) { :public }
|
||||
|
||||
before do
|
||||
status.account.suspend!
|
||||
end
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when status is public' do
|
||||
@ -54,12 +79,10 @@ RSpec.describe ActivityPub::RepliesController, type: :controller do
|
||||
it_behaves_like 'cachable response'
|
||||
|
||||
it 'returns items with account\'s own replies' do
|
||||
json = body_as_json
|
||||
|
||||
expect(json[:first]).to be_a Hash
|
||||
expect(json[:first][:items]).to be_an Array
|
||||
expect(json[:first][:items].size).to eq 1
|
||||
expect(json[:first][:items].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) }).to be true
|
||||
expect(body[:first]).to be_a Hash
|
||||
expect(body[:first][:items]).to be_an Array
|
||||
expect(body[:first][:items].size).to eq 1
|
||||
expect(body[:first][:items].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) }).to be true
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -111,7 +111,7 @@ RSpec.describe Api::V1::Admin::AccountsController, type: :controller do
|
||||
|
||||
describe 'POST #unsuspend' do
|
||||
before do
|
||||
account.touch(:suspended_at)
|
||||
account.suspend!
|
||||
post :unsuspend, params: { id: account.id }
|
||||
end
|
||||
|
||||
|
@ -14,6 +14,27 @@ describe FollowerAccountsController do
|
||||
context 'when format is html' do
|
||||
subject(:response) { get :index, params: { account_username: alice.username, format: :html } }
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
alice.suspend!
|
||||
alice.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
alice.suspend!
|
||||
end
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
it 'assigns follows' do
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
@ -48,6 +69,27 @@ describe FollowerAccountsController do
|
||||
expect(body['totalItems']).to eq 2
|
||||
expect(body['partOf']).to be_present
|
||||
end
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
alice.suspend!
|
||||
alice.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
alice.suspend!
|
||||
end
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'without page' do
|
||||
@ -58,6 +100,27 @@ describe FollowerAccountsController do
|
||||
expect(body['totalItems']).to eq 2
|
||||
expect(body['partOf']).to be_blank
|
||||
end
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
alice.suspend!
|
||||
alice.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
alice.suspend!
|
||||
end
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -14,6 +14,27 @@ describe FollowingAccountsController do
|
||||
context 'when format is html' do
|
||||
subject(:response) { get :index, params: { account_username: alice.username, format: :html } }
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
alice.suspend!
|
||||
alice.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
alice.suspend!
|
||||
end
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
it 'assigns follows' do
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
@ -48,6 +69,27 @@ describe FollowingAccountsController do
|
||||
expect(body['totalItems']).to eq 2
|
||||
expect(body['partOf']).to be_present
|
||||
end
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
alice.suspend!
|
||||
alice.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
alice.suspend!
|
||||
end
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'without page' do
|
||||
@ -58,6 +100,27 @@ describe FollowingAccountsController do
|
||||
expect(body['totalItems']).to eq 2
|
||||
expect(body['partOf']).to be_blank
|
||||
end
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
alice.suspend!
|
||||
alice.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
alice.suspend!
|
||||
end
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -94,21 +94,42 @@ describe RemoteFollowController do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with a suspended account' do
|
||||
context 'with a permanently suspended account' do
|
||||
before do
|
||||
@account = Fabricate(:account, suspended: true)
|
||||
@account = Fabricate(:account)
|
||||
@account.suspend!
|
||||
@account.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns 410 gone on GET to #new' do
|
||||
it 'returns http gone on GET to #new' do
|
||||
get :new, params: { account_username: @account.to_param }
|
||||
|
||||
expect(response).to have_http_status(:gone)
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
|
||||
it 'returns 410 gone on POST to #create' do
|
||||
it 'returns http gone on POST to #create' do
|
||||
post :create, params: { account_username: @account.to_param }
|
||||
|
||||
expect(response).to have_http_status(:gone)
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a temporarily suspended account' do
|
||||
before do
|
||||
@account = Fabricate(:account)
|
||||
@account.suspend!
|
||||
end
|
||||
|
||||
it 'returns http forbidden on GET to #new' do
|
||||
get :new, params: { account_username: @account.to_param }
|
||||
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
|
||||
it 'returns http forbidden on POST to #create' do
|
||||
post :create, params: { account_username: @account.to_param }
|
||||
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -24,10 +24,11 @@ describe StatusesController do
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:status) { Fabricate(:status, account: account) }
|
||||
|
||||
context 'when account is suspended' do
|
||||
let(:account) { Fabricate(:account, suspended: true) }
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
account.deletion_request.destroy
|
||||
|
||||
get :show, params: { account_username: account.username, id: status.id }
|
||||
end
|
||||
|
||||
@ -36,6 +37,18 @@ describe StatusesController do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
|
||||
get :show, params: { account_username: account.username, id: status.id }
|
||||
end
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when status is a reblog' do
|
||||
let(:original_account) { Fabricate(:account, domain: 'example.com') }
|
||||
let(:original_status) { Fabricate(:status, account: original_account, url: 'https://example.com/123') }
|
||||
@ -676,10 +689,11 @@ describe StatusesController do
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:status) { Fabricate(:status, account: account) }
|
||||
|
||||
context 'when account is suspended' do
|
||||
let(:account) { Fabricate(:account, suspended: true) }
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
account.deletion_request.destroy
|
||||
|
||||
get :activity, params: { account_username: account.username, id: status.id }
|
||||
end
|
||||
|
||||
@ -688,6 +702,18 @@ describe StatusesController do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
|
||||
get :activity, params: { account_username: account.username, id: status.id }
|
||||
end
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when status is public' do
|
||||
pending
|
||||
end
|
||||
|
@ -4,95 +4,134 @@ describe WellKnown::WebfingerController, type: :controller do
|
||||
render_views
|
||||
|
||||
describe 'GET #show' do
|
||||
let(:alice) do
|
||||
Fabricate(:account, username: 'alice')
|
||||
end
|
||||
|
||||
before do
|
||||
alice.private_key = <<-PEM
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXQIBAAKBgQDHgPoPJlrfMZrVcuF39UbVssa8r4ObLP3dYl9Y17Mgp5K4mSYD
|
||||
R/Y2ag58tSi6ar2zM3Ze3QYsNfTq0NqN1g89eAu0MbSjWqpOsgntRPJiFuj3hai2
|
||||
X2Im8TBrkiM/UyfTRgn8q8WvMoKbXk8Lu6nqv420eyqhhLxfUoCpxuem1QIDAQAB
|
||||
AoGBAIKsOh2eM7spVI8mdgQKheEG/iEsnPkQ2R8ehfE9JzjmSbXbqghQJDaz9NU+
|
||||
G3Uu4R31QT0VbCudE9SSA/UPFl82GeQG4QLjrSE+PSjSkuslgSXelJHfAJ+ycGax
|
||||
ajtPyiQD0e4c2loagHNHPjqK9OhHx9mFnZWmoagjlZ+mQGEpAkEA8GtqfS65IaRQ
|
||||
uVhMzpp25rF1RWOwaaa+vBPkd7pGdJEQGFWkaR/a9UkU+2C4ZxGBkJDP9FApKVQI
|
||||
RANEwN3/hwJBANRuw5+es6BgBv4PD387IJvuruW2oUtYP+Lb2Z5k77J13hZTr0db
|
||||
Oo9j1UbbR0/4g+vAcsDl4JD9c/9LrGYEpcMCQBon9Yvs+2M3lziy7JhFoc3zXIjS
|
||||
Ea1M4M9hcqe78lJYPeIH3z04o/+vlcLLgQRlmSz7NESmO/QtGkEcAezhuh0CQHji
|
||||
pzO4LeO/gXslut3eGcpiYuiZquOjToecMBRwv+5AIKd367Che4uJdh6iPcyGURvh
|
||||
IewfZFFdyZqnx20ui90CQQC1W2rK5Y30wAunOtSLVA30TLK/tKrTppMC3corjKlB
|
||||
FTX8IvYBNTbpEttc1VCf/0ccnNpfb0CrFNSPWxRj7t7D
|
||||
-----END RSA PRIVATE KEY-----
|
||||
PEM
|
||||
|
||||
alice.public_key = <<-PEM
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHgPoPJlrfMZrVcuF39UbVssa8
|
||||
r4ObLP3dYl9Y17Mgp5K4mSYDR/Y2ag58tSi6ar2zM3Ze3QYsNfTq0NqN1g89eAu0
|
||||
MbSjWqpOsgntRPJiFuj3hai2X2Im8TBrkiM/UyfTRgn8q8WvMoKbXk8Lu6nqv420
|
||||
eyqhhLxfUoCpxuem1QIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
PEM
|
||||
|
||||
alice.save!
|
||||
end
|
||||
let(:alternate_domains) { [] }
|
||||
let(:alice) { Fabricate(:account, username: 'alice') }
|
||||
let(:resource) { nil }
|
||||
|
||||
around(:each) do |example|
|
||||
before = Rails.configuration.x.alternate_domains
|
||||
tmp = Rails.configuration.x.alternate_domains
|
||||
Rails.configuration.x.alternate_domains = alternate_domains
|
||||
example.run
|
||||
Rails.configuration.x.alternate_domains = before
|
||||
Rails.configuration.x.alternate_domains = tmp
|
||||
end
|
||||
|
||||
it 'returns JSON when account can be found' do
|
||||
get :show, params: { resource: alice.to_webfinger_s }, format: :json
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.content_type).to eq 'application/jrd+json'
|
||||
expect(json[:subject]).to eq 'acct:alice@cb6e6126.ngrok.io'
|
||||
expect(json[:aliases]).to include('https://cb6e6126.ngrok.io/@alice', 'https://cb6e6126.ngrok.io/users/alice')
|
||||
subject do
|
||||
get :show, params: { resource: resource }, format: :json
|
||||
end
|
||||
|
||||
it 'returns http not found when account cannot be found' do
|
||||
get :show, params: { resource: 'acct:not@existing.com' }, format: :json
|
||||
shared_examples 'a successful response' do
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
expect(response).to have_http_status(:not_found)
|
||||
it 'returns application/jrd+json' do
|
||||
expect(response.content_type).to eq 'application/jrd+json'
|
||||
end
|
||||
|
||||
it 'returns links for the account' do
|
||||
json = body_as_json
|
||||
expect(json[:subject]).to eq 'acct:alice@cb6e6126.ngrok.io'
|
||||
expect(json[:aliases]).to include('https://cb6e6126.ngrok.io/@alice', 'https://cb6e6126.ngrok.io/users/alice')
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns JSON when account can be found with alternate domains' do
|
||||
Rails.configuration.x.alternate_domains = ['foo.org']
|
||||
username, = alice.to_webfinger_s.split('@')
|
||||
context 'when an account exists' do
|
||||
let(:resource) { alice.to_webfinger_s }
|
||||
|
||||
get :show, params: { resource: "#{username}@foo.org" }, format: :json
|
||||
before do
|
||||
subject
|
||||
end
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.content_type).to eq 'application/jrd+json'
|
||||
expect(json[:subject]).to eq 'acct:alice@cb6e6126.ngrok.io'
|
||||
expect(json[:aliases]).to include('https://cb6e6126.ngrok.io/@alice', 'https://cb6e6126.ngrok.io/users/alice')
|
||||
it_behaves_like 'a successful response'
|
||||
end
|
||||
|
||||
it 'returns http not found when account can not be found with alternate domains' do
|
||||
Rails.configuration.x.alternate_domains = ['foo.org']
|
||||
username, = alice.to_webfinger_s.split('@')
|
||||
context 'when an account is temporarily suspended' do
|
||||
let(:resource) { alice.to_webfinger_s }
|
||||
|
||||
get :show, params: { resource: "#{username}@bar.org" }, format: :json
|
||||
before do
|
||||
alice.suspend!
|
||||
subject
|
||||
end
|
||||
|
||||
expect(response).to have_http_status(:not_found)
|
||||
it_behaves_like 'a successful response'
|
||||
end
|
||||
|
||||
it 'returns http bad request when not given a resource parameter' do
|
||||
get :show, params: { }, format: :json
|
||||
expect(response).to have_http_status(:bad_request)
|
||||
context 'when an account is permanently suspended or deleted' do
|
||||
let(:resource) { alice.to_webfinger_s }
|
||||
|
||||
before do
|
||||
alice.suspend!
|
||||
alice.deletion_request.destroy
|
||||
subject
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns http bad request when given a nonsense parameter' do
|
||||
get :show, params: { resource: 'df/:dfkj' }
|
||||
expect(response).to have_http_status(:bad_request)
|
||||
context 'when an account is not found' do
|
||||
let(:resource) { 'acct:not@existing.com' }
|
||||
|
||||
before do
|
||||
subject
|
||||
end
|
||||
|
||||
it 'returns http not found' do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an alternate domain' do
|
||||
let(:alternate_domains) { ['foo.org'] }
|
||||
|
||||
before do
|
||||
subject
|
||||
end
|
||||
|
||||
context 'when an account exists' do
|
||||
let(:resource) do
|
||||
username, = alice.to_webfinger_s.split('@')
|
||||
"#{username}@foo.org"
|
||||
end
|
||||
|
||||
it_behaves_like 'a successful response'
|
||||
end
|
||||
|
||||
context 'when the domain is wrong' do
|
||||
let(:resource) do
|
||||
username, = alice.to_webfinger_s.split('@')
|
||||
"#{username}@bar.org"
|
||||
end
|
||||
|
||||
it 'returns http not found' do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with no resource parameter' do
|
||||
let(:resource) { nil }
|
||||
|
||||
before do
|
||||
subject
|
||||
end
|
||||
|
||||
it 'returns http bad request' do
|
||||
expect(response).to have_http_status(400)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a nonsense parameter' do
|
||||
let(:resource) { 'df/:dfkj' }
|
||||
|
||||
before do
|
||||
subject
|
||||
end
|
||||
|
||||
it 'returns http bad request' do
|
||||
expect(response).to have_http_status(400)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -7,8 +7,9 @@ RSpec.describe AccountPolicy do
|
||||
let(:subject) { described_class }
|
||||
let(:admin) { Fabricate(:user, admin: true).account }
|
||||
let(:john) { Fabricate(:user).account }
|
||||
let(:alice) { Fabricate(:user).account }
|
||||
|
||||
permissions :index?, :show?, :unsuspend?, :unsensitive?, :unsilence?, :remove_avatar?, :remove_header? do
|
||||
permissions :index? do
|
||||
context 'staff' do
|
||||
it 'permits' do
|
||||
expect(subject).to permit(admin)
|
||||
@ -22,6 +23,38 @@ RSpec.describe AccountPolicy do
|
||||
end
|
||||
end
|
||||
|
||||
permissions :show?, :unsilence?, :unsensitive?, :remove_avatar?, :remove_header? do
|
||||
context 'staff' do
|
||||
it 'permits' do
|
||||
expect(subject).to permit(admin, alice)
|
||||
end
|
||||
end
|
||||
|
||||
context 'not staff' do
|
||||
it 'denies' do
|
||||
expect(subject).to_not permit(john, alice)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
permissions :unsuspend? do
|
||||
before do
|
||||
alice.suspend!
|
||||
end
|
||||
|
||||
context 'staff' do
|
||||
it 'permits' do
|
||||
expect(subject).to permit(admin, alice)
|
||||
end
|
||||
end
|
||||
|
||||
context 'not staff' do
|
||||
it 'denies' do
|
||||
expect(subject).to_not permit(john, alice)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
permissions :redownload?, :subscribe?, :unsubscribe? do
|
||||
context 'admin' do
|
||||
it 'permits' do
|
||||
|
@ -73,4 +73,84 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do
|
||||
expect(ProofProvider::Keybase::Worker).to have_received(:perform_async)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is not suspended' do
|
||||
let!(:account) { Fabricate(:account, username: 'alice', domain: 'example.com') }
|
||||
|
||||
let(:payload) do
|
||||
{
|
||||
id: 'https://foo.test',
|
||||
type: 'Actor',
|
||||
inbox: 'https://foo.test/inbox',
|
||||
suspended: true,
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
before do
|
||||
allow(Admin::SuspensionWorker).to receive(:perform_async)
|
||||
end
|
||||
|
||||
subject { described_class.new.call('alice', 'example.com', payload) }
|
||||
|
||||
it 'suspends account remotely' do
|
||||
expect(subject.suspended?).to be true
|
||||
expect(subject.suspension_origin_remote?).to be true
|
||||
end
|
||||
|
||||
it 'queues suspension worker' do
|
||||
subject
|
||||
expect(Admin::SuspensionWorker).to have_received(:perform_async)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is suspended' do
|
||||
let!(:account) { Fabricate(:account, username: 'alice', domain: 'example.com', display_name: '') }
|
||||
|
||||
let(:payload) do
|
||||
{
|
||||
id: 'https://foo.test',
|
||||
type: 'Actor',
|
||||
inbox: 'https://foo.test/inbox',
|
||||
suspended: false,
|
||||
name: 'Hoge',
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
before do
|
||||
allow(Admin::UnsuspensionWorker).to receive(:perform_async)
|
||||
|
||||
account.suspend!(origin: suspension_origin)
|
||||
end
|
||||
|
||||
subject { described_class.new.call('alice', 'example.com', payload) }
|
||||
|
||||
context 'locally' do
|
||||
let(:suspension_origin) { :local }
|
||||
|
||||
it 'does not unsuspend it' do
|
||||
expect(subject.suspended?).to be true
|
||||
end
|
||||
|
||||
it 'does not update any attributes' do
|
||||
expect(subject.display_name).to_not eq 'Hoge'
|
||||
end
|
||||
end
|
||||
|
||||
context 'remotely' do
|
||||
let(:suspension_origin) { :remote }
|
||||
|
||||
it 'unsuspends it' do
|
||||
expect(subject.suspended?).to be false
|
||||
end
|
||||
|
||||
it 'queues unsuspension worker' do
|
||||
subject
|
||||
expect(Admin::UnsuspensionWorker).to have_received(:perform_async)
|
||||
end
|
||||
|
||||
it 'updates attributes' do
|
||||
expect(subject.display_name).to eq 'Hoge'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -22,7 +22,48 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
|
||||
subject { described_class.new }
|
||||
|
||||
describe '#call' do
|
||||
context 'when actor is the sender'
|
||||
context 'when actor is suspended' do
|
||||
before do
|
||||
actor.suspend!(origin: :remote)
|
||||
end
|
||||
|
||||
%w(Accept Add Announce Block Create Flag Follow Like Move Remove).each do |activity_type|
|
||||
context "with #{activity_type} activity" do
|
||||
let(:payload) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: 'foo',
|
||||
type: activity_type,
|
||||
actor: ActivityPub::TagManager.instance.uri_for(actor),
|
||||
}
|
||||
end
|
||||
|
||||
it 'does not process payload' do
|
||||
expect(ActivityPub::Activity).not_to receive(:factory)
|
||||
subject.call(json, actor)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
%w(Delete Reject Undo Update).each do |activity_type|
|
||||
context "with #{activity_type} activity" do
|
||||
let(:payload) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: 'foo',
|
||||
type: activity_type,
|
||||
actor: ActivityPub::TagManager.instance.uri_for(actor),
|
||||
}
|
||||
end
|
||||
|
||||
it 'processes the payload' do
|
||||
expect(ActivityPub::Activity).to receive(:factory)
|
||||
subject.call(json, actor)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when actor differs from sender' do
|
||||
let(:forwarder) { Fabricate(:account, domain: 'example.com', uri: 'http://example.com/other_account') }
|
||||
|
||||
|
@ -13,16 +13,41 @@ RSpec.describe ResolveAccountService, type: :service do
|
||||
stub_request(:get, "https://ap.example.com/users/foo").to_return(request_fixture('activitypub-actor.txt'))
|
||||
stub_request(:get, "https://ap.example.com/users/foo.atom").to_return(request_fixture('activitypub-feed.txt'))
|
||||
stub_request(:get, %r{https://ap.example.com/users/foo/\w+}).to_return(status: 404)
|
||||
stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:hoge@example.com').to_return(status: 410)
|
||||
end
|
||||
|
||||
it 'raises error if no such user can be resolved via webfinger' do
|
||||
it 'returns nil if no such user can be resolved via webfinger' do
|
||||
expect(subject.call('catsrgr8@quitter.no')).to be_nil
|
||||
end
|
||||
|
||||
it 'raises error if the domain does not have webfinger' do
|
||||
it 'returns nil if the domain does not have webfinger' do
|
||||
expect(subject.call('catsrgr8@example.com')).to be_nil
|
||||
end
|
||||
|
||||
context 'when webfinger returns http gone' do
|
||||
context 'for a previously known account' do
|
||||
before do
|
||||
Fabricate(:account, username: 'hoge', domain: 'example.com', last_webfingered_at: nil)
|
||||
allow(AccountDeletionWorker).to receive(:perform_async)
|
||||
end
|
||||
|
||||
it 'returns nil' do
|
||||
expect(subject.call('hoge@example.com')).to be_nil
|
||||
end
|
||||
|
||||
it 'queues account deletion worker' do
|
||||
subject.call('hoge@example.com')
|
||||
expect(AccountDeletionWorker).to have_received(:perform_async)
|
||||
end
|
||||
end
|
||||
|
||||
context 'for a previously unknown account' do
|
||||
it 'returns nil' do
|
||||
expect(subject.call('hoge@example.com')).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an ActivityPub account' do
|
||||
it 'returns new remote account' do
|
||||
account = subject.call('foo@ap.example.com')
|
||||
|
Reference in New Issue
Block a user