mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
FEATURE: Show error if invite to topic is invalid (#15959)
This can happen if the topic to which a user is invited is in a private category and the user was not invited to one of the groups that can see that specific category. This used to be a warning and this commit makes it an error.
This commit is contained in:
parent
34e2ed6d76
commit
effbd6d3e4
@ -121,7 +121,7 @@ export default Controller.extend(
|
|||||||
|
|
||||||
return this.invite
|
return this.invite
|
||||||
.save(data)
|
.save(data)
|
||||||
.then((result) => {
|
.then(() => {
|
||||||
this.rollbackBuffer();
|
this.rollbackBuffer();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -131,22 +131,14 @@ export default Controller.extend(
|
|||||||
this.invites.unshiftObject(this.invite);
|
this.invites.unshiftObject(this.invite);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.warnings) {
|
if (this.isEmail && opts.sendEmail) {
|
||||||
|
this.send("closeModal");
|
||||||
|
} else {
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
flashText: sanitize(result.warnings.join(",")),
|
flashText: sanitize(I18n.t("user.invited.invite.invite_saved")),
|
||||||
flashClass: "warning",
|
flashClass: "success",
|
||||||
flashLink: !this.editing,
|
flashLink: !this.editing,
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
if (this.isEmail && opts.sendEmail) {
|
|
||||||
this.send("closeModal");
|
|
||||||
} else {
|
|
||||||
this.setProperties({
|
|
||||||
flashText: sanitize(I18n.t("user.invited.invite.invite_saved")),
|
|
||||||
flashClass: "success",
|
|
||||||
flashLink: !this.editing,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((e) =>
|
.catch((e) =>
|
||||||
|
@ -105,7 +105,10 @@ export default Controller.extend(
|
|||||||
inviteUsers() {
|
inviteUsers() {
|
||||||
this.set("showNotifyUsers", false);
|
this.set("showNotifyUsers", false);
|
||||||
const controller = showModal("create-invite");
|
const controller = showModal("create-invite");
|
||||||
controller.set("inviteToTopic", true);
|
controller.setProperties({
|
||||||
|
inviteToTopic: true,
|
||||||
|
topics: [this.topic],
|
||||||
|
});
|
||||||
controller.buffered.setProperties({
|
controller.buffered.setProperties({
|
||||||
topicId: this.topic.id,
|
topicId: this.topic.id,
|
||||||
topicTitle: this.topic.title,
|
topicTitle: this.topic.title,
|
||||||
|
@ -138,6 +138,11 @@ class InvitesController < ApplicationController
|
|||||||
|
|
||||||
guardian.ensure_can_invite_to_forum!(groups)
|
guardian.ensure_can_invite_to_forum!(groups)
|
||||||
|
|
||||||
|
if !groups_can_see_topic?(groups, topic)
|
||||||
|
editable_topic_groups = topic.category.groups.filter { |g| guardian.can_edit_group?(g) }
|
||||||
|
return render_json_error(I18n.t("invite.requires_groups", groups: editable_topic_groups.pluck(:name).join(", ")))
|
||||||
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
invite = Invite.generate(current_user,
|
invite = Invite.generate(current_user,
|
||||||
email: params[:email],
|
email: params[:email],
|
||||||
@ -201,6 +206,11 @@ class InvitesController < ApplicationController
|
|||||||
groups.each { |group| invite.invited_groups.find_or_create_by!(group_id: group.id) } if groups.present?
|
groups.each { |group| invite.invited_groups.find_or_create_by!(group_id: group.id) } if groups.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if !groups_can_see_topic?(invite.groups, invite.topics.first)
|
||||||
|
editable_topic_groups = invite.topics.first.category.groups.filter { |g| guardian.can_edit_group?(g) }
|
||||||
|
return render_json_error(I18n.t("invite.requires_groups", groups: editable_topic_groups.pluck(:name).join(", ")))
|
||||||
|
end
|
||||||
|
|
||||||
if params.has_key?(:email)
|
if params.has_key?(:email)
|
||||||
old_email = invite.email.presence
|
old_email = invite.email.presence
|
||||||
new_email = params[:email].presence
|
new_email = params[:email].presence
|
||||||
@ -448,6 +458,15 @@ class InvitesController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def groups_can_see_topic?(groups, topic)
|
||||||
|
if topic&.read_restricted_category?
|
||||||
|
topic_groups = topic.category.groups
|
||||||
|
return false if (groups & topic_groups).blank?
|
||||||
|
end
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
def post_process_invite(user)
|
def post_process_invite(user)
|
||||||
user.enqueue_welcome_message('welcome_invite') if user.send_welcome_message
|
user.enqueue_welcome_message('welcome_invite') if user.send_welcome_message
|
||||||
|
|
||||||
|
@ -239,23 +239,6 @@ class Invite < ActiveRecord::Base
|
|||||||
Jobs.enqueue(:invite_email, invite_id: self.id)
|
Jobs.enqueue(:invite_email, invite_id: self.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def warnings(guardian)
|
|
||||||
@warnings ||= begin
|
|
||||||
warnings = []
|
|
||||||
|
|
||||||
topic = self.topics.first
|
|
||||||
if topic&.read_restricted_category?
|
|
||||||
topic_groups = topic.category.groups
|
|
||||||
if (self.groups & topic_groups).blank?
|
|
||||||
editable_topic_groups = topic_groups.filter { |g| guardian.can_edit_group?(g) }
|
|
||||||
warnings << I18n.t("invite.requires_groups", groups: editable_topic_groups.pluck(:name).join(", "))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
warnings
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def limit_invites_per_day
|
def limit_invites_per_day
|
||||||
RateLimiter.new(invited_by, "invites-per-day", SiteSetting.max_invites_per_day, 1.day.to_i)
|
RateLimiter.new(invited_by, "invites-per-day", SiteSetting.max_invites_per_day, 1.day.to_i)
|
||||||
end
|
end
|
||||||
|
@ -13,8 +13,7 @@ class InviteSerializer < ApplicationSerializer
|
|||||||
:created_at,
|
:created_at,
|
||||||
:updated_at,
|
:updated_at,
|
||||||
:expires_at,
|
:expires_at,
|
||||||
:expired,
|
:expired
|
||||||
:warnings
|
|
||||||
|
|
||||||
has_many :topics, embed: :object, serializer: BasicTopicSerializer
|
has_many :topics, embed: :object, serializer: BasicTopicSerializer
|
||||||
has_many :groups, embed: :object, serializer: BasicGroupSerializer
|
has_many :groups, embed: :object, serializer: BasicGroupSerializer
|
||||||
@ -46,12 +45,4 @@ class InviteSerializer < ApplicationSerializer
|
|||||||
def expired
|
def expired
|
||||||
object.expired?
|
object.expired?
|
||||||
end
|
end
|
||||||
|
|
||||||
def warnings
|
|
||||||
object.warnings(scope)
|
|
||||||
end
|
|
||||||
|
|
||||||
def include_warnings?
|
|
||||||
object.warnings(scope).present?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -258,7 +258,7 @@ en:
|
|||||||
disabled_errors:
|
disabled_errors:
|
||||||
discourse_connect_enabled: "Invites are disabled because DiscourseConnect is enabled."
|
discourse_connect_enabled: "Invites are disabled because DiscourseConnect is enabled."
|
||||||
invalid_access: "You are not permitted to view the requested resource."
|
invalid_access: "You are not permitted to view the requested resource."
|
||||||
requires_groups: "Invite saved. To give access to the specified topic, add one of the following groups: %{groups}."
|
requires_groups: "Invite was not saved because the specified topic is inaccessible. Add one of the following groups: %{groups}."
|
||||||
domain_not_allowed: "Your email cannot be used to redeem this invite."
|
domain_not_allowed: "Your email cannot be used to redeem this invite."
|
||||||
|
|
||||||
bulk_invite:
|
bulk_invite:
|
||||||
|
@ -429,27 +429,4 @@ describe Invite do
|
|||||||
expect(invite.invalidated_at).to be_nil
|
expect(invite.invalidated_at).to be_nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#warnings' do
|
|
||||||
fab!(:admin) { Fabricate(:admin) }
|
|
||||||
fab!(:invite) { Fabricate(:invite) }
|
|
||||||
fab!(:group) { Fabricate(:group) }
|
|
||||||
fab!(:secured_category) do
|
|
||||||
secured_category = Fabricate(:category)
|
|
||||||
secured_category.permissions = { group.name => :full }
|
|
||||||
secured_category.save!
|
|
||||||
secured_category
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not return any warnings for simple invites' do
|
|
||||||
expect(invite.warnings(admin.guardian)).to be_blank
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns a warning if topic is private' do
|
|
||||||
topic = Fabricate(:topic, category: secured_category)
|
|
||||||
TopicInvite.create!(topic: topic, invite: invite)
|
|
||||||
|
|
||||||
expect(invite.warnings(admin.guardian)).to contain_exactly(I18n.t("invite.requires_groups", groups: group.name))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -203,6 +203,35 @@ describe InvitesController do
|
|||||||
post '/invites.json', params: { email: 'test@example.com', topic_id: -9999 }
|
post '/invites.json', params: { email: 'test@example.com', topic_id: -9999 }
|
||||||
expect(response.status).to eq(400)
|
expect(response.status).to eq(400)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'topic is private' do
|
||||||
|
fab!(:group) { Fabricate(:group) }
|
||||||
|
|
||||||
|
fab!(:secured_category) do |category|
|
||||||
|
category = Fabricate(:category)
|
||||||
|
category.permissions = { group.name => :full }
|
||||||
|
category.save!
|
||||||
|
category
|
||||||
|
end
|
||||||
|
|
||||||
|
fab!(:topic) { Fabricate(:topic, category: secured_category) }
|
||||||
|
|
||||||
|
it 'does not work and returns a list of required groups' do
|
||||||
|
sign_in(admin)
|
||||||
|
|
||||||
|
post '/invites.json', params: { email: 'test@example.com', topic_id: topic.id }
|
||||||
|
expect(response.status).to eq(422)
|
||||||
|
expect(response.parsed_body["errors"]).to contain_exactly(I18n.t("invite.requires_groups", groups: group.name))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not work if user cannot edit groups' do
|
||||||
|
group.add(user)
|
||||||
|
sign_in(user)
|
||||||
|
|
||||||
|
post '/invites.json', params: { email: 'test@example.com', topic_id: topic.id }
|
||||||
|
expect(response.status).to eq(403)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'invite to group' do
|
context 'invite to group' do
|
||||||
|
Loading…
Reference in New Issue
Block a user