mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
SECURITY: Fix invite link email validation (#18817)
See https://github.com/discourse/discourse/security/advisories/GHSA-x8w7-rwmr-w278 Co-authored-by: Martin Brennan <martin@discourse.org>
This commit is contained in:
@@ -242,6 +242,40 @@ RSpec.describe InviteRedeemer do
|
||||
expect(invite.invited_users.first).to be_present
|
||||
end
|
||||
|
||||
it "raises an error if the email does not match the invite email" do
|
||||
redeemer = InviteRedeemer.new(invite: invite, email: "blah@test.com", username: username, name: name)
|
||||
expect { redeemer.redeem }.to raise_error(ActiveRecord::RecordNotSaved, I18n.t("invite.not_matching_email"))
|
||||
end
|
||||
|
||||
context "when a redeeming user is passed in" do
|
||||
fab!(:redeeming_user) { Fabricate(:user, email: "foobar@example.com") }
|
||||
|
||||
it "raises an error if the email does not match the invite email" do
|
||||
redeeming_user.update!(email: "foo@bar.com")
|
||||
redeemer = InviteRedeemer.new(invite: invite, redeeming_user: redeeming_user)
|
||||
expect { redeemer.redeem }.to raise_error(ActiveRecord::RecordNotSaved, I18n.t("invite.not_matching_email"))
|
||||
end
|
||||
end
|
||||
|
||||
context 'with domain' do
|
||||
fab!(:invite) { Fabricate(:invite, email: nil, domain: "test.com") }
|
||||
|
||||
it "raises an error if the email domain does not match the invite domain" do
|
||||
redeemer = InviteRedeemer.new(invite: invite, email: "blah@somesite.com", username: username, name: name)
|
||||
expect { redeemer.redeem }.to raise_error(ActiveRecord::RecordNotSaved, I18n.t("invite.domain_not_allowed"))
|
||||
end
|
||||
|
||||
context "when a redeeming user is passed in" do
|
||||
fab!(:redeeming_user) { Fabricate(:user, email: "foo@test.com") }
|
||||
|
||||
it "raises an error if the user's email domain does not match the invite domain" do
|
||||
redeeming_user.update!(email: "foo@bar.com")
|
||||
redeemer = InviteRedeemer.new(invite: invite, redeeming_user: redeeming_user)
|
||||
expect { redeemer.redeem }.to raise_error(ActiveRecord::RecordNotSaved, I18n.t("invite.domain_not_allowed"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invite_link' do
|
||||
fab!(:invite_link) { Fabricate(:invite, email: nil, max_redemptions_allowed: 5, expires_at: 1.month.from_now, emailed_status: Invite.emailed_status_types[:not_required]) }
|
||||
let(:invite_redeemer) { InviteRedeemer.new(invite: invite_link, email: 'foo@example.com') }
|
||||
@@ -257,7 +291,7 @@ RSpec.describe InviteRedeemer do
|
||||
end
|
||||
|
||||
it "should not redeem the invite if InvitedUser record already exists for email" do
|
||||
user = invite_redeemer.redeem
|
||||
invite_redeemer.redeem
|
||||
invite_link.reload
|
||||
|
||||
another_invite_redeemer = InviteRedeemer.new(invite: invite_link, email: 'foo@example.com')
|
||||
@@ -266,14 +300,28 @@ RSpec.describe InviteRedeemer do
|
||||
end
|
||||
|
||||
it "should redeem the invite if InvitedUser record does not exists for email" do
|
||||
user = invite_redeemer.redeem
|
||||
invite_redeemer.redeem
|
||||
invite_link.reload
|
||||
|
||||
another_invite_redeemer = InviteRedeemer.new(invite: invite_link, email: 'bar@example.com')
|
||||
another_user = another_invite_redeemer.redeem
|
||||
expect(another_user.is_a?(User)).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
it "raises an error if the email is already being used by an existing user" do
|
||||
Fabricate(:user, email: 'foo@example.com')
|
||||
expect { invite_redeemer.redeem }.to raise_error(ActiveRecord::RecordInvalid, /Primary email has already been taken/)
|
||||
end
|
||||
|
||||
context "when a redeeming user is passed in" do
|
||||
fab!(:redeeming_user) { Fabricate(:user, email: 'foo@example.com') }
|
||||
|
||||
it "does not create a new user" do
|
||||
expect do
|
||||
InviteRedeemer.new(invite: invite_link, redeeming_user: redeeming_user).redeem
|
||||
end.not_to change { User.count }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -338,38 +338,38 @@ RSpec.describe Invite do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#redeem_from_email' do
|
||||
describe '#redeem_for_existing_user' do
|
||||
fab!(:invite) { Fabricate(:invite, email: 'test@example.com') }
|
||||
fab!(:user) { Fabricate(:user, email: invite.email) }
|
||||
|
||||
it 'redeems the invite from email' do
|
||||
Invite.redeem_from_email(user.email)
|
||||
Invite.redeem_for_existing_user(user)
|
||||
expect(invite.reload).to be_redeemed
|
||||
end
|
||||
|
||||
it 'does not redeem the invite if email does not match' do
|
||||
Invite.redeem_from_email('test2@example.com')
|
||||
user.update!(email: 'test2@example.com')
|
||||
Invite.redeem_for_existing_user(user)
|
||||
expect(invite.reload).not_to be_redeemed
|
||||
end
|
||||
|
||||
it 'does not work with expired invites' do
|
||||
invite.update!(expires_at: 1.day.ago)
|
||||
Invite.redeem_from_email(user.email)
|
||||
Invite.redeem_for_existing_user(user)
|
||||
expect(invite).not_to be_redeemed
|
||||
end
|
||||
|
||||
it 'does not work with deleted invites' do
|
||||
invite.trash!
|
||||
Invite.redeem_from_email(user.email)
|
||||
Invite.redeem_for_existing_user(user)
|
||||
expect(invite).not_to be_redeemed
|
||||
end
|
||||
|
||||
it 'does not work with invalidated invites' do
|
||||
invite.update!(invalidated_at: 1.day.ago)
|
||||
Invite.redeem_from_email(user.email)
|
||||
Invite.redeem_for_existing_user(user)
|
||||
expect(invite).not_to be_redeemed
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe 'scopes' do
|
||||
|
||||
Reference in New Issue
Block a user