mirror of
https://github.com/discourse/discourse.git
synced 2025-03-06 11:19:51 +00:00
SECURITY: Handle concurrent invite accepts
Raise an error on concurrent invite accept attempts.
This commit is contained in:
parent
bfc3132bb2
commit
62a609ea2d
@ -278,7 +278,10 @@ class InvitesController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
user = invite.redeem(**attrs)
|
user = invite.redeem(**attrs)
|
||||||
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved, Invite::UserExists => e
|
rescue ActiveRecord::RecordInvalid,
|
||||||
|
ActiveRecord::RecordNotSaved,
|
||||||
|
ActiveRecord::LockWaitTimeout,
|
||||||
|
Invite::UserExists => e
|
||||||
return render json: failed_json.merge(message: e.message), status: 412
|
return render json: failed_json.merge(message: e.message), status: 412
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -243,7 +243,10 @@ class InviteRedeemer
|
|||||||
@invited_user_record = InvitedUser.create!(invite_id: invite.id, redeemed_at: Time.zone.now)
|
@invited_user_record = InvitedUser.create!(invite_id: invite.id, redeemed_at: Time.zone.now)
|
||||||
|
|
||||||
if @invited_user_record.present?
|
if @invited_user_record.present?
|
||||||
Invite.increment_counter(:redemption_count, invite.id)
|
invite.with_lock("FOR UPDATE NOWAIT") do
|
||||||
|
Invite.increment_counter(:redemption_count, invite.id)
|
||||||
|
invite.save!
|
||||||
|
end
|
||||||
delete_duplicate_invites
|
delete_duplicate_invites
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -984,6 +984,27 @@ RSpec.describe InvitesController do
|
|||||||
Fabricate(:invite, email: nil, emailed_status: Invite.emailed_status_types[:not_required])
|
Fabricate(:invite, email: nil, emailed_status: Invite.emailed_status_types[:not_required])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "does not create multiple users for a single use invite" do
|
||||||
|
user_count = User.count
|
||||||
|
|
||||||
|
2
|
||||||
|
.times
|
||||||
|
.map do
|
||||||
|
Thread.new do
|
||||||
|
put "/invites/show/#{invite.invite_key}.json",
|
||||||
|
params: {
|
||||||
|
email: "test@example.com",
|
||||||
|
password: "verystrongpassword",
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
.each(&:join)
|
||||||
|
|
||||||
|
expect(invite.reload.max_redemptions_allowed).to eq(1)
|
||||||
|
expect(invite.reload.redemption_count).to eq(1)
|
||||||
|
expect(User.count).to eq(user_count + 1)
|
||||||
|
end
|
||||||
|
|
||||||
it "sends an activation email and does not activate the user" do
|
it "sends an activation email and does not activate the user" do
|
||||||
expect {
|
expect {
|
||||||
put "/invites/show/#{invite.invite_key}.json",
|
put "/invites/show/#{invite.invite_key}.json",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user