SECURITY: Require groups to be given when inviting to a restricted category. (#6715)

This commit is contained in:
Guo Xiang Tan 2018-12-05 23:43:07 +08:00 committed by Guo Xiang Tan
parent e8b51feceb
commit cffb3d7976
12 changed files with 308 additions and 195 deletions

View File

@ -289,7 +289,7 @@ export default Ember.Controller.extend(ModalFunctionality, {
model.setProperties({ saving: true, error: false }); model.setProperties({ saving: true, error: false });
const onerror = function(e) { const onerror = e => {
if (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors) { if (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors) {
self.set("errorMessage", e.jqXHR.responseJSON.errors[0]); self.set("errorMessage", e.jqXHR.responseJSON.errors[0]);
} else { } else {

View File

@ -555,7 +555,7 @@ class TopicsController < ApplicationController
)) ))
end end
guardian.ensure_can_invite_to!(topic, groups) guardian.ensure_can_invite_to!(topic)
group_ids = groups.map(&:id) group_ids = groups.map(&:id)
begin begin
@ -568,7 +568,25 @@ class TopicsController < ApplicationController
render json: success_json render json: success_json
end end
else else
render json: failed_json, status: 422 json = failed_json
unless topic.private_message?
group_names = topic.category
.visible_group_names(current_user)
.where(automatic: false)
.pluck(:name)
.join(", ")
if group_names.present?
json.merge!(errors: [
I18n.t("topic_invite.failed_to_invite",
group_names: group_names
)
])
end
end
render json: json, status: 422
end end
rescue Topic::UserExists => e rescue Topic::UserExists => e
render json: { errors: [e.message] }, status: 422 render json: { errors: [e.message] }, status: 422

View File

@ -475,6 +475,10 @@ class Category < ActiveRecord::Base
self.name_lower = name.downcase if self.name self.name_lower = name.downcase if self.name
end end
def visible_group_names(user)
self.groups.visible_groups(user)
end
def secure_group_ids def secure_group_ids
if self.read_restricted? if self.read_restricted?
groups.pluck("groups.id") groups.pluck("groups.id")

View File

@ -57,19 +57,6 @@ class Invite < ActiveRecord::Base
InviteRedeemer.new(self, username, name, password, user_custom_fields).redeem unless expired? || destroyed? || !link_valid? InviteRedeemer.new(self, username, name, password, user_custom_fields).redeem unless expired? || destroyed? || !link_valid?
end end
def self.extend_permissions(topic, user, invited_by)
if topic.private_message?
topic.grant_permission_to_user(user.email)
elsif topic.category && topic.category.groups.any?
if Guardian.new(invited_by).can_invite_via_email?(topic)
(topic.category.groups - user.groups).each do |group|
group.add(user)
GroupActionLogger.new(Discourse.system_user, group).log_add_user_to_group(user)
end
end
end
end
def self.invite_by_email(email, invited_by, topic = nil, group_ids = nil, custom_message = nil) def self.invite_by_email(email, invited_by, topic = nil, group_ids = nil, custom_message = nil)
create_invite_by_email(email, invited_by, create_invite_by_email(email, invited_by,
topic: topic, topic: topic,
@ -103,8 +90,10 @@ class Invite < ActiveRecord::Base
lower_email = Email.downcase(email) lower_email = Email.downcase(email)
if user = find_user_by_email(lower_email) if user = find_user_by_email(lower_email)
extend_permissions(topic, user, invited_by) if topic raise UserExists.new(I18n.t("invite.user_exists",
raise UserExists.new I18n.t("invite.user_exists", email: lower_email, username: user.username) email: lower_email,
username: user.username
))
end end
invite = Invite.with_deleted invite = Invite.with_deleted
@ -134,14 +123,10 @@ class Invite < ActiveRecord::Base
if group_ids.present? if group_ids.present?
group_ids = group_ids - invite.invited_groups.pluck(:group_id) group_ids = group_ids - invite.invited_groups.pluck(:group_id)
group_ids.each do |group_id| group_ids.each do |group_id|
invite.invited_groups.create!(group_id: group_id) invite.invited_groups.create!(group_id: group_id)
end end
else
if topic && topic.category && Guardian.new(invited_by).can_invite_to?(topic)
group_ids = topic.category.groups.where(automatic: false).pluck(:id) - invite.invited_groups.pluck(:group_id)
group_ids.each { |group_id| invite.invited_groups.create!(group_id: group_id) }
end
end end
Jobs.enqueue(:invite_email, invite_id: invite.id) if send_email Jobs.enqueue(:invite_email, invite_id: invite.id) if send_email

View File

@ -840,62 +840,29 @@ class Topic < ActiveRecord::Base
def invite(invited_by, username_or_email, group_ids = nil, custom_message = nil) def invite(invited_by, username_or_email, group_ids = nil, custom_message = nil)
target_user = User.find_by_username_or_email(username_or_email) target_user = User.find_by_username_or_email(username_or_email)
guardian = Guardian.new(invited_by) guardian = Guardian.new(invited_by)
is_email = username_or_email =~ /^.+@.+$/
if target_user && topic_allowed_users.where(user_id: target_user.id).exists? if target_user
raise UserExists.new(I18n.t("topic_invite.user_exists")) if topic_allowed_users.exists?(user_id: target_user.id)
end raise UserExists.new(I18n.t("topic_invite.user_exists"))
return true if target_user && invite_existing_muted?(target_user, invited_by)
if private_message? && target_user && !guardian.can_send_private_message?(target_user)
raise UserExists.new(I18n.t("activerecord.errors.models.topic.attributes.base.cant_send_pm"))
end
if target_user && private_message? && topic_allowed_users.create!(user_id: target_user.id)
rate_limit_topic_invitation(invited_by)
add_small_action(invited_by, "invited_user", target_user.username)
create_invite_notification!(
target_user,
Notification.types[:invited_to_private_message],
invited_by.username
)
true
elsif username_or_email =~ /^.+@.+$/ && guardian.can_invite_via_email?(self)
if target_user
rate_limit_topic_invitation(invited_by)
Invite.extend_permissions(self, target_user, invited_by)
create_invite_notification!(
target_user,
Notification.types[:invited_to_topic],
invited_by.username
)
else
invite_by_email(invited_by, username_or_email, group_ids, custom_message)
end end
true if invite_existing_muted?(target_user, invited_by)
elsif target_user && return true
rate_limit_topic_invitation(invited_by) && end
topic_allowed_users.create!(user_id: target_user.id)
create_invite_notification!( if private_message?
target_user, !!invite_to_private_message(invited_by, target_user, guardian)
Notification.types[:invited_to_topic], else
invited_by.username !!invite_to_topic(invited_by, target_user, group_ids, guardian)
end
elsif is_email && guardian.can_invite_via_email?(self)
!!Invite.invite_by_email(
username_or_email, invited_by, self, group_ids, custom_message
) )
true
end end
end end
def invite_by_email(invited_by, email, group_ids = nil, custom_message = nil)
Invite.invite_by_email(email, invited_by, self, group_ids, custom_message)
end
def invite_existing_muted?(target_user, invited_by) def invite_existing_muted?(target_user, invited_by)
if invited_by.id && if invited_by.id &&
MutedUser.where(user_id: target_user.id, muted_user_id: invited_by.id) MutedUser.where(user_id: target_user.id, muted_user_id: invited_by.id)
@ -1398,6 +1365,55 @@ class Topic < ActiveRecord::Base
private private
def invite_to_private_message(invited_by, target_user, guardian)
if !guardian.can_send_private_message?(target_user)
raise UserExists.new(I18n.t(
"activerecord.errors.models.topic.attributes.base.cant_send_pm"
))
end
Topic.transaction do
rate_limit_topic_invitation(invited_by)
topic_allowed_users.create!(user_id: target_user.id)
add_small_action(invited_by, "invited_user", target_user.username)
create_invite_notification!(
target_user,
Notification.types[:invited_to_private_message],
invited_by.username
)
end
end
def invite_to_topic(invited_by, target_user, group_ids, guardian)
Topic.transaction do
rate_limit_topic_invitation(invited_by)
if group_ids
(
self.category.groups.where(id: group_ids).where(automatic: false) -
target_user.groups.where(automatic: false)
).each do |group|
if guardian.can_edit_group?(group)
group.add(target_user)
GroupActionLogger
.new(invited_by, group)
.log_add_user_to_group(target_user)
end
end
end
if Guardian.new(target_user).can_see_topic?(self)
create_invite_notification!(
target_user,
Notification.types[:invited_to_topic],
invited_by.username
)
end
end
end
def update_category_topic_count_by(num) def update_category_topic_count_by(num)
if category_id.present? if category_id.present?
Category.where(['id = ?', category_id]).update_all("topic_count = topic_count " + (num > 0 ? '+' : '') + "#{num}") Category.where(['id = ?', category_id]).update_all("topic_count = topic_count " + (num > 0 ? '+' : '') + "#{num}")

View File

@ -190,6 +190,7 @@ en:
error: "There was an error uploading that file. Please try again later." error: "There was an error uploading that file. Please try again later."
topic_invite: topic_invite:
failed_to_invite: "The user cannot be invited into this topic without a group membership in either one of the following groups: %{group_names}."
user_exists: "Sorry, that user has already been invited. You may only invite a user to a topic once." user_exists: "Sorry, that user has already been invited. You may only invite a user to a topic once."
backup: backup:

View File

@ -265,19 +265,25 @@ class Guardian
def can_invite_to?(object, groups = nil) def can_invite_to?(object, groups = nil)
return false unless authenticated? return false unless authenticated?
return true if is_admin? is_topic = object.is_a?(Topic)
return true if is_admin? && !is_topic
return false if (SiteSetting.max_invites_per_day.to_i == 0 && !is_staff?) return false if (SiteSetting.max_invites_per_day.to_i == 0 && !is_staff?)
return false unless can_see?(object) return false unless can_see?(object)
return false if groups.present? return false if groups.present?
if object.is_a?(Topic) && object.private_message? if is_topic
return false unless SiteSetting.enable_personal_messages? if object.private_message?
return false if object.reached_recipients_limit? && !is_staff? return true if is_admin?
end return false unless SiteSetting.enable_personal_messages?
return false if object.reached_recipients_limit? && !is_staff?
end
if object.is_a?(Topic) && object.category if (category = object.category) && category.read_restricted
if object.category.groups.any? if (groups = category.groups&.where(automatic: false))&.any?
return true if object.category.groups.all? { |g| can_edit_group?(g) } return groups.any? { |g| can_edit_group?(g) } ? true : false
else
return false
end
end end
end end

View File

@ -550,8 +550,8 @@ describe Guardian do
expect(Guardian.new(user).can_invite_to?(private_topic)).to be_falsey expect(Guardian.new(user).can_invite_to?(private_topic)).to be_falsey
end end
it 'returns true for admin on private topic' do it 'returns false for admin on private topic' do
expect(Guardian.new(admin).can_invite_to?(private_topic)).to be_truthy expect(Guardian.new(admin).can_invite_to?(private_topic)).to be(false)
end end
it 'returns true for a group owner' do it 'returns true for a group owner' do
@ -562,6 +562,49 @@ describe Guardian do
SiteSetting.enable_personal_messages = false SiteSetting.enable_personal_messages = false
expect(Guardian.new(trust_level_2).can_invite_to?(topic)).to be_truthy expect(Guardian.new(trust_level_2).can_invite_to?(topic)).to be_truthy
end end
describe 'for a private category for automatic and non-automatic group' do
let(:automatic_group) { Fabricate(:group, automatic: true) }
let(:group) { Fabricate(:group) }
let(:category) do
Fabricate(:category, read_restricted: true).tap do |category|
category.groups << automatic_group
category.groups << group
end
end
let(:topic) { Fabricate(:topic, category: category) }
it 'should return true for an admin user' do
expect(Guardian.new(admin).can_invite_to?(topic)).to eq(true)
end
it 'should return true for a group owner' do
expect(Guardian.new(group_owner).can_invite_to?(topic)).to eq(true)
end
it 'should return false for a normal user' do
expect(Guardian.new(user).can_invite_to?(topic)).to eq(false)
end
end
describe 'for a private category for automatic groups' do
let(:group) { Fabricate(:group, automatic: true) }
let(:category) do
Fabricate(:private_category, group: group, read_restricted: true)
end
let(:group_owner) { Fabricate(:user).tap { |user| group.add_owner(user) } }
let(:topic) { Fabricate(:topic, category: category) }
it 'should return false for all type of users' do
expect(Guardian.new(admin).can_invite_to?(topic)).to eq(false)
expect(Guardian.new(group_owner).can_invite_to?(topic)).to eq(false)
expect(Guardian.new(user).can_invite_to?(topic)).to eq(false)
end
end
end end
describe "private messages" do describe "private messages" do

View File

@ -59,37 +59,38 @@ describe Invite do
context 'email' do context 'email' do
it 'enqueues a job to email the invite' do it 'enqueues a job to email the invite' do
Jobs.expects(:enqueue).with(:invite_email, has_key(:invite_id)) expect do
topic.invite_by_email(inviter, iceking) Invite.invite_by_email(iceking, inviter, topic)
end.to change { Jobs::InviteEmail.jobs.size }
end end
end end
context 'destroyed' do context 'destroyed' do
it "can invite the same user after their invite was destroyed" do it "can invite the same user after their invite was destroyed" do
invite = topic.invite_by_email(inviter, iceking) Invite.invite_by_email(iceking, inviter, topic).destroy!
invite.destroy invite = Invite.invite_by_email(iceking, inviter, topic)
invite = topic.invite_by_email(inviter, iceking)
expect(invite).to be_present expect(invite).to be_present
end end
end end
context 'after created' do context 'after created' do
before do let(:invite) { Invite.invite_by_email(iceking, inviter, topic) }
@invite = topic.invite_by_email(inviter, iceking)
end
it 'belongs to the topic' do it 'belongs to the topic' do
expect(topic.invites).to eq([@invite]) expect(topic.invites).to eq([invite])
expect(@invite.topics).to eq([topic]) expect(invite.topics).to eq([topic])
end end
context 'when added by another user' do context 'when added by another user' do
let(:coding_horror) { Fabricate(:coding_horror) } let(:coding_horror) { Fabricate(:coding_horror) }
let(:new_invite) { topic.invite_by_email(coding_horror, iceking) }
let(:new_invite) do
Invite.invite_by_email(iceking, coding_horror, topic)
end
it 'returns a different invite' do it 'returns a different invite' do
expect(new_invite).not_to eq(@invite) expect(new_invite).not_to eq(invite)
expect(new_invite.invite_key).not_to eq(@invite.invite_key) expect(new_invite.invite_key).not_to eq(invite.invite_key)
expect(new_invite.topics).to eq([topic]) expect(new_invite.topics).to eq([topic])
end end
@ -97,24 +98,36 @@ describe Invite do
context 'when adding a duplicate' do context 'when adding a duplicate' do
it 'returns the original invite' do it 'returns the original invite' do
expect(topic.invite_by_email(inviter, 'iceking@adventuretime.ooo')).to eq(@invite) %w{
expect(topic.invite_by_email(inviter, 'iceking@ADVENTURETIME.ooo')).to eq(@invite) iceking@adventuretime.ooo
expect(topic.invite_by_email(inviter, 'ICEKING@adventuretime.ooo')).to eq(@invite) iceking@ADVENTURETIME.ooo
ICEKING@adventuretime.ooo
}.each do |email|
expect(Invite.invite_by_email(
email, inviter, topic
)).to eq(invite)
end
end end
it 'updates timestamp of existing invite' do it 'updates timestamp of existing invite' do
@invite.created_at = 10.days.ago invite.update!(created_at: 10.days.ago)
@invite.save
resend_invite = topic.invite_by_email(inviter, 'iceking@adventuretime.ooo') resend_invite = Invite.invite_by_email(
'iceking@adventuretime.ooo', inviter, topic
)
expect(resend_invite.created_at).to be_within(1.minute).of(Time.zone.now) expect(resend_invite.created_at).to be_within(1.minute).of(Time.zone.now)
end end
it 'returns a new invite if the other has expired' do it 'returns a new invite if the other has expired' do
SiteSetting.invite_expiry_days = 1 SiteSetting.invite_expiry_days = 1
@invite.created_at = 2.days.ago invite.update!(created_at: 2.days.ago)
@invite.save
new_invite = topic.invite_by_email(inviter, 'iceking@adventuretime.ooo') new_invite = Invite.invite_by_email(
expect(new_invite).not_to eq(@invite) 'iceking@adventuretime.ooo', inviter, topic
)
expect(new_invite).not_to eq(invite)
expect(new_invite).not_to be_expired expect(new_invite).not_to be_expired
end end
end end
@ -123,64 +136,24 @@ describe Invite do
let!(:another_topic) { Fabricate(:topic, user: topic.user) } let!(:another_topic) { Fabricate(:topic, user: topic.user) }
it 'should be the same invite' do it 'should be the same invite' do
@new_invite = another_topic.invite_by_email(inviter, iceking) new_invite = Invite.invite_by_email(iceking, inviter, another_topic)
expect(@new_invite).to eq(@invite) expect(new_invite).to eq(invite)
expect(another_topic.invites).to eq([@invite]) expect(another_topic.invites).to eq([invite])
expect(@invite.topics).to match_array([topic, another_topic]) expect(invite.topics).to match_array([topic, another_topic])
end end
end end
end end
end end
end end
context 'to a group-private topic' do
let(:group) { Fabricate(:group) }
let(:private_category) { Fabricate(:private_category, group: group) }
let(:group_private_topic) { Fabricate(:topic, category: private_category) }
let(:inviter) { group_private_topic.user }
before do
group.add_owner(inviter)
@invite = group_private_topic.invite_by_email(inviter, iceking)
end
it 'should add the groups to the invite' do
expect(@invite.groups).to eq([group])
end
context 'when duplicated' do
it 'should not duplicate the groups' do
expect(group_private_topic.invite_by_email(inviter, iceking)).to eq(@invite)
expect(@invite.groups).to eq([group])
end
end
it 'verifies that inviter is authorized to invite user to a topic' do
tl2_user = Fabricate(:user, trust_level: 2)
invite = group_private_topic.invite_by_email(tl2_user, 'foo@bar.com')
expect(invite.groups.count).to eq(0)
end
context 'automatic groups' do
it 'should not add invited user to automatic groups' do
group.update!(automatic: true)
expect(group_private_topic.invite_by_email(Fabricate(:admin), iceking).groups.count).to eq(0)
end
end
end
context 'an existing user' do context 'an existing user' do
let(:topic) { Fabricate(:topic, category_id: nil, archetype: 'private_message') } let(:topic) { Fabricate(:topic, category_id: nil, archetype: 'private_message') }
let(:coding_horror) { Fabricate(:coding_horror) } let(:coding_horror) { Fabricate(:coding_horror) }
it "works" do it "works" do
# doesn't create an invite expect do
expect { topic.invite_by_email(topic.user, coding_horror.email) }.to raise_error(Invite::UserExists) Invite.invite_by_email(coding_horror.email, topic.user, topic)
end.to raise_error(Invite::UserExists)
# gives the user permission to access the topic
expect(topic.allowed_users.include?(coding_horror)).to eq(true)
end end
end end
@ -353,7 +326,6 @@ describe Invite do
it 'adds the user to the topic_users of the first topic' do it 'adds the user to the topic_users of the first topic' do
expect(another_topic.invite(another_tl2_user, user.username)).to be_truthy # invited via username expect(another_topic.invite(another_tl2_user, user.username)).to be_truthy # invited via username
expect(topic.allowed_users.include?(user)).to eq(true) expect(topic.allowed_users.include?(user)).to eq(true)
expect(another_topic.allowed_users.include?(user)).to eq(true)
end end
end end
end end

View File

@ -608,35 +608,85 @@ describe Topic do
end end
describe 'public topic' do describe 'public topic' do
def expect_the_right_notification_to_be_created def expect_the_right_notification_to_be_created(inviter, invitee)
notification = Notification.last notification = Notification.last
expect(notification.notification_type) expect(notification.notification_type)
.to eq(Notification.types[:invited_to_topic]) .to eq(Notification.types[:invited_to_topic])
expect(notification.user).to eq(another_user) expect(notification.user).to eq(invitee)
expect(notification.topic).to eq(topic) expect(notification.topic).to eq(topic)
notification_data = JSON.parse(notification.data) notification_data = JSON.parse(notification.data)
expect(notification_data["topic_title"]).to eq(topic.title) expect(notification_data["topic_title"]).to eq(topic.title)
expect(notification_data["display_username"]).to eq(user.username) expect(notification_data["display_username"]).to eq(inviter.username)
end end
describe 'by username' do describe 'by username' do
it 'should invite user into a topic' do it 'should invite user into a topic' do
topic.invite(user, another_user.username) topic.invite(user, another_user.username)
expect_the_right_notification_to_be_created(user, another_user)
expect(topic.reload.allowed_users.last).to eq(another_user)
expect_the_right_notification_to_be_created
end end
end end
describe 'by email' do describe 'by email' do
it 'should be able to invite a user' do it 'should be able to invite a user' do
expect(topic.invite(user, another_user.email)).to eq(true) expect(topic.invite(user, another_user.email)).to eq(true)
expect(topic.reload.allowed_users.last).to eq(another_user) expect_the_right_notification_to_be_created(user, another_user)
expect_the_right_notification_to_be_created end
describe 'when topic belongs to a private category' do
let(:group) { Fabricate(:group) }
let(:category) do
Fabricate(:category, groups: [group]).tap do |category|
category.set_permissions(group => :full)
category.save!
end
end
let(:topic) { Fabricate(:topic, category: category) }
let(:inviter) { Fabricate(:user).tap { |user| group.add_owner(user) } }
let(:invitee) { Fabricate(:user) }
describe 'as a group owner' do
it 'should be able to invite a user' do
expect do
expect(topic.invite(inviter, invitee.email, [group.id]))
.to eq(true)
end.to change { Notification.count } &
change { GroupHistory.count }
expect_the_right_notification_to_be_created(inviter, invitee)
group_history = GroupHistory.last
expect(group_history.acting_user).to eq(inviter)
expect(group_history.target_user).to eq(invitee)
expect(group_history.action).to eq(
GroupHistory.actions[:add_user_to_group]
)
end
describe 'when group ids are not given' do
it 'should not invite the user' do
expect do
expect(topic.invite(inviter, invitee.email)).to eq(false)
end.to_not change { Notification.count }
end
end
end
describe 'as a normal user' do
it 'should not be able to invite a user' do
expect do
expect(topic.invite(Fabricate(:user), invitee.email, [group.id]))
.to eq(false)
end.to_not change { Notification.count }
end
end
end end
context "for a muted topic" do context "for a muted topic" do
@ -2004,34 +2054,6 @@ describe Topic do
expect(topic.save).to eq(true) expect(topic.save).to eq(true)
end end
context 'invite by group manager' do
let(:group_manager) { Fabricate(:user) }
let(:group) { Fabricate(:group).tap { |g| g.add_owner(group_manager) } }
let(:private_category) { Fabricate(:private_category, group: group) }
let(:group_private_topic) { Fabricate(:topic, category: private_category, user: group_manager) }
context 'to an email' do
let(:randolph) { 'randolph@duke.ooo' }
it "should attach group to the invite" do
group_private_topic.invite(group_manager, randolph)
expect(Invite.last.groups).to eq([group])
end
end
# should work for an existing user - give access, send notification
context 'to an existing user' do
let(:walter) { Fabricate(:walter_white) }
it "should add user to the group" do
expect(Guardian.new(walter).can_see?(group_private_topic)).to be_falsey
group_private_topic.invite(group_manager, walter.email)
expect(walter.groups).to include(group)
expect(Guardian.new(walter).can_see?(group_private_topic)).to be_truthy
end
end
end
it "Correctly sets #message_archived?" do it "Correctly sets #message_archived?" do
topic = Fabricate(:private_message_topic) topic = Fabricate(:private_message_topic)
user = topic.user user = topic.user

View File

@ -215,9 +215,13 @@ describe InvitesController do
context 'with a deleted invite' do context 'with a deleted invite' do
let(:topic) { Fabricate(:topic) } let(:topic) { Fabricate(:topic) }
let(:invite) { topic.invite_by_email(topic.user, "iceking@adventuretime.ooo") }
let(:invite) do
Invite.invite_by_email("iceking@adventuretime.ooo", topic.user, topic)
end
before do before do
invite.destroy invite.destroy!
end end
it "redirects to the root" do it "redirects to the root" do
@ -233,7 +237,9 @@ describe InvitesController do
context 'with a valid invite id' do context 'with a valid invite id' do
let(:topic) { Fabricate(:topic) } let(:topic) { Fabricate(:topic) }
let(:invite) { topic.invite_by_email(topic.user, "iceking@adventuretime.ooo") } let(:invite) do
Invite.invite_by_email("iceking@adventuretime.ooo", topic.user, topic)
end
it 'redeems the invite' do it 'redeems the invite' do
put "/invites/show/#{invite.invite_key}.json" put "/invites/show/#{invite.invite_key}.json"
@ -323,7 +329,11 @@ describe InvitesController do
context 'new registrations are disabled' do context 'new registrations are disabled' do
let(:topic) { Fabricate(:topic) } let(:topic) { Fabricate(:topic) }
let(:invite) { topic.invite_by_email(topic.user, "iceking@adventuretime.ooo") }
let(:invite) do
Invite.invite_by_email("iceking@adventuretime.ooo", topic.user, topic)
end
before { SiteSetting.allow_new_registrations = false } before { SiteSetting.allow_new_registrations = false }
it "doesn't redeem the invite" do it "doesn't redeem the invite" do
@ -338,7 +348,11 @@ describe InvitesController do
context 'user is already logged in' do context 'user is already logged in' do
let(:topic) { Fabricate(:topic) } let(:topic) { Fabricate(:topic) }
let(:invite) { topic.invite_by_email(topic.user, "iceking@adventuretime.ooo") }
let(:invite) do
Invite.invite_by_email("iceking@adventuretime.ooo", topic.user, topic)
end
let!(:user) { sign_in(Fabricate(:user)) } let!(:user) { sign_in(Fabricate(:user)) }
it "doesn't redeem the invite" do it "doesn't redeem the invite" do

View File

@ -2010,14 +2010,46 @@ RSpec.describe TopicsController do
let(:recipient) { 'jake@adventuretime.ooo' } let(:recipient) { 'jake@adventuretime.ooo' }
it "should attach group to the invite" do it "should attach group to the invite" do
post "/t/#{group_private_topic.id}/invite.json", params: { post "/t/#{group_private_topic.id}/invite.json", params: {
user: recipient user: recipient,
group_ids: "#{group.id},123"
} }
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(Invite.find_by(email: recipient).groups).to eq([group]) expect(Invite.find_by(email: recipient).groups).to eq([group])
end end
describe 'when group is available to automatic groups only' do
before do
group.update!(automatic: true)
end
it 'should return the right response' do
post "/t/#{group_private_topic.id}/invite.json", params: {
user: Fabricate(:user)
}
expect(response.status).to eq(403)
end
end
describe 'when user is not part of the required group' do
it 'should return the right response' do
post "/t/#{group_private_topic.id}/invite.json", params: {
user: Fabricate(:user)
}
expect(response.status).to eq(422)
response_body = JSON.parse(response.body)
expect(response_body["errors"]).to eq([
I18n.t("topic_invite.failed_to_invite",
group_names: group.name
)
])
end
end
end end
describe 'when topic id is invalid' do describe 'when topic id is invalid' do