Merge remote-tracking branch 'parent/main' into upstream-20240124
This commit is contained in:
commit
54f5113b46
106 changed files with 1396 additions and 1071 deletions
|
@ -265,21 +265,35 @@ RSpec.describe Auth::SessionsController do
|
|||
context 'when repeatedly using an invalid TOTP code before using a valid code' do
|
||||
before do
|
||||
stub_const('Auth::SessionsController::MAX_2FA_ATTEMPTS_PER_HOUR', 2)
|
||||
|
||||
# Travel to the beginning of an hour to avoid crossing rate-limit buckets
|
||||
travel_to '2023-12-20T10:00:00Z'
|
||||
end
|
||||
|
||||
it 'does not log the user in' do
|
||||
# Travel to the beginning of an hour to avoid crossing rate-limit buckets
|
||||
travel_to '2023-12-20T10:00:00Z'
|
||||
|
||||
Auth::SessionsController::MAX_2FA_ATTEMPTS_PER_HOUR.times do
|
||||
post :create, params: { user: { otp_attempt: '1234' } }, session: { attempt_user_id: user.id, attempt_user_updated_at: user.updated_at.to_s }
|
||||
expect(controller.current_user).to be_nil
|
||||
end
|
||||
|
||||
post :create, params: { user: { otp_attempt: user.current_otp } }, session: { attempt_user_id: user.id, attempt_user_updated_at: user.updated_at.to_s }
|
||||
|
||||
expect(controller.current_user).to be_nil
|
||||
expect(flash[:alert]).to match I18n.t('users.rate_limited')
|
||||
end
|
||||
|
||||
it 'sends a suspicious sign-in mail', :sidekiq_inline do
|
||||
Auth::SessionsController::MAX_2FA_ATTEMPTS_PER_HOUR.times do
|
||||
post :create, params: { user: { otp_attempt: '1234' } }, session: { attempt_user_id: user.id, attempt_user_updated_at: user.updated_at.to_s }
|
||||
expect(controller.current_user).to be_nil
|
||||
end
|
||||
|
||||
post :create, params: { user: { otp_attempt: user.current_otp } }, session: { attempt_user_id: user.id, attempt_user_updated_at: user.updated_at.to_s }
|
||||
|
||||
expect(UserMailer.deliveries.size).to eq(1)
|
||||
expect(UserMailer.deliveries.first.to.first).to eq(user.email)
|
||||
expect(UserMailer.deliveries.first.subject).to eq(I18n.t('user_mailer.failed_2fa.subject'))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when using a valid OTP' do
|
||||
|
|
|
@ -93,4 +93,9 @@ class UserMailerPreview < ActionMailer::Preview
|
|||
def suspicious_sign_in
|
||||
UserMailer.suspicious_sign_in(User.first, '127.0.0.1', 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:75.0) Gecko/20100101 Firefox/75.0', Time.now.utc)
|
||||
end
|
||||
|
||||
# Preview this email at http://localhost:3000/rails/mailers/user_mailer/failed_2fa
|
||||
def failed_2fa
|
||||
UserMailer.failed_2fa(User.first, '127.0.0.1', 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:75.0) Gecko/20100101 Firefox/75.0', Time.now.utc)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -135,6 +135,24 @@ describe UserMailer do
|
|||
'user_mailer.suspicious_sign_in.subject'
|
||||
end
|
||||
|
||||
describe '#failed_2fa' do
|
||||
let(:ip) { '192.168.0.1' }
|
||||
let(:agent) { 'NCSA_Mosaic/2.0 (Windows 3.1)' }
|
||||
let(:timestamp) { Time.now.utc }
|
||||
let(:mail) { described_class.failed_2fa(receiver, ip, agent, timestamp) }
|
||||
|
||||
it 'renders failed 2FA notification' do
|
||||
receiver.update!(locale: nil)
|
||||
|
||||
expect(mail)
|
||||
.to be_present
|
||||
.and(have_body_text(I18n.t('user_mailer.failed_2fa.explanation')))
|
||||
end
|
||||
|
||||
include_examples 'localized subject',
|
||||
'user_mailer.failed_2fa.subject'
|
||||
end
|
||||
|
||||
describe '#appeal_approved' do
|
||||
let(:appeal) { Fabricate(:appeal, account: receiver.account, approved_at: Time.now.utc) }
|
||||
let(:mail) { described_class.appeal_approved(receiver, appeal) }
|
||||
|
|
|
@ -1046,6 +1046,31 @@ RSpec.describe Account do
|
|||
end
|
||||
|
||||
describe 'scopes' do
|
||||
describe 'matches_uri_prefix' do
|
||||
let!(:alice) { Fabricate :account, domain: 'host.example', uri: 'https://host.example/user/a' }
|
||||
let!(:bob) { Fabricate :account, domain: 'top-level.example', uri: 'https://top-level.example' }
|
||||
|
||||
it 'returns accounts which start with the value' do
|
||||
results = described_class.matches_uri_prefix('https://host.example')
|
||||
|
||||
expect(results.size)
|
||||
.to eq(1)
|
||||
expect(results)
|
||||
.to include(alice)
|
||||
.and not_include(bob)
|
||||
end
|
||||
|
||||
it 'returns accounts which equal the value' do
|
||||
results = described_class.matches_uri_prefix('https://top-level.example')
|
||||
|
||||
expect(results.size)
|
||||
.to eq(1)
|
||||
expect(results)
|
||||
.to include(bob)
|
||||
.and not_include(alice)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'auditable' do
|
||||
let!(:alice) { Fabricate :account }
|
||||
let!(:bob) { Fabricate :account }
|
||||
|
|
|
@ -100,6 +100,38 @@ RSpec.describe Tag do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.recently_used' do
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:other_person_status) { Fabricate(:status) }
|
||||
let(:out_of_range) { Fabricate(:status, account: account) }
|
||||
let(:older_in_range) { Fabricate(:status, account: account) }
|
||||
let(:newer_in_range) { Fabricate(:status, account: account) }
|
||||
let(:unused_tag) { Fabricate(:tag) }
|
||||
let(:used_tag_one) { Fabricate(:tag) }
|
||||
let(:used_tag_two) { Fabricate(:tag) }
|
||||
let(:used_tag_on_out_of_range) { Fabricate(:tag) }
|
||||
|
||||
before do
|
||||
stub_const 'Tag::RECENT_STATUS_LIMIT', 2
|
||||
|
||||
other_person_status.tags << used_tag_one
|
||||
|
||||
out_of_range.tags << used_tag_on_out_of_range
|
||||
|
||||
older_in_range.tags << used_tag_one
|
||||
older_in_range.tags << used_tag_two
|
||||
|
||||
newer_in_range.tags << used_tag_one
|
||||
end
|
||||
|
||||
it 'returns tags used by account within last X statuses ordered most used first' do
|
||||
results = described_class.recently_used(account)
|
||||
|
||||
expect(results)
|
||||
.to eq([used_tag_one, used_tag_two])
|
||||
end
|
||||
end
|
||||
|
||||
describe '.find_normalized' do
|
||||
it 'returns tag for a multibyte case-insensitive name' do
|
||||
upcase_string = 'abcABCabcABCやゆよ'
|
||||
|
|
|
@ -2,21 +2,21 @@
|
|||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Api::V1::Statuses::FavouritedByAccountsController do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:app) { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, application: app, scopes: 'read:accounts') }
|
||||
RSpec.describe 'API V1 Statuses Favourited by Accounts' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:scopes) { 'read:accounts' }
|
||||
# let(:app) { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
let(:alice) { Fabricate(:account) }
|
||||
let(:bob) { Fabricate(:account) }
|
||||
|
||||
context 'with an oauth token' do
|
||||
before do
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
subject do
|
||||
get "/api/v1/statuses/#{status.id}/favourited_by", headers: headers, params: { limit: 2 }
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
describe 'GET /api/v1/statuses/:status_id/favourited_by' do
|
||||
let(:status) { Fabricate(:status, account: user.account) }
|
||||
|
||||
before do
|
||||
|
@ -24,30 +24,38 @@ RSpec.describe Api::V1::Statuses::FavouritedByAccountsController do
|
|||
Favourite.create!(account: bob, status: status)
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
get :index, params: { status_id: status.id, limit: 2 }
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.headers['Link'].links.size).to eq(2)
|
||||
end
|
||||
it 'returns http success and accounts who favourited the status' do
|
||||
subject
|
||||
|
||||
it 'returns accounts who favorited the status' do
|
||||
get :index, params: { status_id: status.id, limit: 2 }
|
||||
expect(body_as_json.size).to eq 2
|
||||
expect([body_as_json[0][:id], body_as_json[1][:id]]).to contain_exactly(alice.id.to_s, bob.id.to_s)
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
expect(response.headers['Link'].links.size)
|
||||
.to eq(2)
|
||||
|
||||
expect(body_as_json.size)
|
||||
.to eq(2)
|
||||
expect(body_as_json)
|
||||
.to contain_exactly(
|
||||
include(id: alice.id.to_s),
|
||||
include(id: bob.id.to_s)
|
||||
)
|
||||
end
|
||||
|
||||
it 'does not return blocked users' do
|
||||
user.account.block!(bob)
|
||||
get :index, params: { status_id: status.id, limit: 2 }
|
||||
expect(body_as_json.size).to eq 1
|
||||
expect(body_as_json[0][:id]).to eq alice.id.to_s
|
||||
|
||||
subject
|
||||
|
||||
expect(body_as_json.size)
|
||||
.to eq 1
|
||||
expect(body_as_json.first[:id]).to eq(alice.id.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'without an oauth token' do
|
||||
before do
|
||||
allow(controller).to receive(:doorkeeper_token).and_return(nil)
|
||||
subject do
|
||||
get "/api/v1/statuses/#{status.id}/favourited_by", params: { limit: 2 }
|
||||
end
|
||||
|
||||
context 'with a private status' do
|
||||
|
@ -59,7 +67,8 @@ RSpec.describe Api::V1::Statuses::FavouritedByAccountsController do
|
|||
end
|
||||
|
||||
it 'returns http unauthorized' do
|
||||
get :index, params: { status_id: status.id }
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
@ -74,7 +83,8 @@ RSpec.describe Api::V1::Statuses::FavouritedByAccountsController do
|
|||
end
|
||||
|
||||
it 'returns http success' do
|
||||
get :index, params: { status_id: status.id }
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
|
@ -2,21 +2,20 @@
|
|||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Api::V1::Statuses::RebloggedByAccountsController do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:app) { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, application: app, scopes: 'read:accounts') }
|
||||
RSpec.describe 'API V1 Statuses Reblogged by Accounts' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:scopes) { 'read:accounts' }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
let(:alice) { Fabricate(:account) }
|
||||
let(:bob) { Fabricate(:account) }
|
||||
|
||||
context 'with an oauth token' do
|
||||
before do
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
subject do
|
||||
get "/api/v1/statuses/#{status.id}/reblogged_by", headers: headers, params: { limit: 2 }
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
describe 'GET /api/v1/statuses/:status_id/reblogged_by' do
|
||||
let(:status) { Fabricate(:status, account: user.account) }
|
||||
|
||||
before do
|
||||
|
@ -25,27 +24,37 @@ RSpec.describe Api::V1::Statuses::RebloggedByAccountsController do
|
|||
end
|
||||
|
||||
it 'returns accounts who reblogged the status', :aggregate_failures do
|
||||
get :index, params: { status_id: status.id, limit: 2 }
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.headers['Link'].links.size).to eq(2)
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
expect(response.headers['Link'].links.size)
|
||||
.to eq(2)
|
||||
|
||||
expect(body_as_json.size).to eq 2
|
||||
expect([body_as_json[0][:id], body_as_json[1][:id]]).to contain_exactly(alice.id.to_s, bob.id.to_s)
|
||||
expect(body_as_json.size)
|
||||
.to eq(2)
|
||||
expect(body_as_json)
|
||||
.to contain_exactly(
|
||||
include(id: alice.id.to_s),
|
||||
include(id: bob.id.to_s)
|
||||
)
|
||||
end
|
||||
|
||||
it 'does not return blocked users' do
|
||||
user.account.block!(bob)
|
||||
get :index, params: { status_id: status.id, limit: 2 }
|
||||
expect(body_as_json.size).to eq 1
|
||||
expect(body_as_json[0][:id]).to eq alice.id.to_s
|
||||
|
||||
subject
|
||||
|
||||
expect(body_as_json.size)
|
||||
.to eq 1
|
||||
expect(body_as_json.first[:id]).to eq(alice.id.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'without an oauth token' do
|
||||
before do
|
||||
allow(controller).to receive(:doorkeeper_token).and_return(nil)
|
||||
subject do
|
||||
get "/api/v1/statuses/#{status.id}/reblogged_by", params: { limit: 2 }
|
||||
end
|
||||
|
||||
context 'with a private status' do
|
||||
|
@ -57,7 +66,8 @@ RSpec.describe Api::V1::Statuses::RebloggedByAccountsController do
|
|||
end
|
||||
|
||||
it 'returns http unauthorized' do
|
||||
get :index, params: { status_id: status.id }
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
@ -72,7 +82,8 @@ RSpec.describe Api::V1::Statuses::RebloggedByAccountsController do
|
|||
end
|
||||
|
||||
it 'returns http success' do
|
||||
get :index, params: { status_id: status.id }
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue