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:
David Taylor
2022-11-01 16:33:32 +00:00
committed by GitHub
parent 68b4fe4cf8
commit 07ef1a80a1
13 changed files with 507 additions and 223 deletions

View File

@@ -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

View File

@@ -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