Commit 3eef5691 authored by Alex Kalderimis's avatar Alex Kalderimis

Ignore group members in user quick actions

This ensures that:

- our error messages are helpful, pointing out exactly what is wrong
- we permit things like commas and 'and'
- we never return users the current user cannot read

We also test that we cannot read mass groups like `@all`.
parent 8110c54a
...@@ -71,21 +71,19 @@ module QuickActions ...@@ -71,21 +71,19 @@ module QuickActions
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def extract_users(params) def extract_users(params)
return [] if params.nil? return [] if params.blank?
args = params.split(' ').uniq args = params.split(/\s|,/).select(&:present?).uniq - ['and']
users = extract_references(params, :user) users = extract_references(args.reject { _1 == 'me' }.join(' '), :user)
users << current_user if args.include?('me')
users = User.where(username: args).to_a if users.empty?
if users.empty? users.select! { can?(:read_user_profile, _1) }
users =
if params.strip == 'me' usernames = users.map(&:username).to_set
[current_user] missing = args.reject { |arg| arg == 'me' || usernames.include?(arg.delete_prefix('@')) }.map { "'#{_1}'" }
else
User.where(username: args)
end
end
failed_parse(format(_("Failed to find users for '%{params}'"), params: params)) if users.size < args.size failed_parse(format(_("Failed to find users for %{missing}"), missing: missing.to_sentence)) if missing.present?
users users
end end
...@@ -194,6 +192,10 @@ module QuickActions ...@@ -194,6 +192,10 @@ module QuickActions
user: current_user user: current_user
) )
end end
def can?(ability, object)
Ability.allowed?(current_user, ability, object)
end
end end
end end
......
...@@ -273,8 +273,8 @@ RSpec.describe Notes::QuickActionsService do ...@@ -273,8 +273,8 @@ RSpec.describe Notes::QuickActionsService do
let(:multiline_assign_reviewer_text) do let(:multiline_assign_reviewer_text) do
<<~HEREDOC <<~HEREDOC
/assign_reviewer @#{user.username} /assign_reviewer #{user.to_reference}
/assign_reviewer @#{reviewer.username}) /assign_reviewer #{reviewer.to_reference}
HEREDOC HEREDOC
end end
......
...@@ -142,12 +142,12 @@ RSpec.describe QuickActions::InterpretService do ...@@ -142,12 +142,12 @@ RSpec.describe QuickActions::InterpretService do
group_members.each { group.add_developer(_1) } group_members.each { group.add_developer(_1) }
end end
it 'asssigns all group members' do it 'ignores group members' do
merge_request.update!(assignee_ids: [user.id]) merge_request.update!(assignee_ids: [user.id])
_, updates = service.execute(command, merge_request) _, updates = service.execute(command, merge_request)
expect(updates[:assignee_ids]).to match_array([user.id] + group_members.map(&:id)) expect(updates[:assignee_ids]).to be_nil
end end
end end
......
...@@ -14921,7 +14921,7 @@ msgstr "" ...@@ -14921,7 +14921,7 @@ msgstr ""
msgid "Failed to find import label for Jira import." msgid "Failed to find import label for Jira import."
msgstr "" msgstr ""
msgid "Failed to find users for '%{params}'" msgid "Failed to find users for %{missing}"
msgstr "" msgstr ""
msgid "Failed to generate export, please try again later." msgid "Failed to generate export, please try again later."
......
...@@ -11,13 +11,14 @@ RSpec.describe QuickActions::InterpretService do ...@@ -11,13 +11,14 @@ RSpec.describe QuickActions::InterpretService do
let_it_be(:developer2) { create(:user) } let_it_be(:developer2) { create(:user) }
let_it_be(:developer3) { create(:user) } let_it_be(:developer3) { create(:user) }
let_it_be_with_reload(:issue) { create(:issue, project: project) } let_it_be_with_reload(:issue) { create(:issue, project: project) }
let(:milestone) { create(:milestone, project: project, title: '9.10') }
let(:commit) { create(:commit, project: project) }
let_it_be(:inprogress) { create(:label, project: project, title: 'In Progress') } let_it_be(:inprogress) { create(:label, project: project, title: 'In Progress') }
let_it_be(:helmchart) { create(:label, project: project, title: 'Helm Chart Registry') } let_it_be(:helmchart) { create(:label, project: project, title: 'Helm Chart Registry') }
let_it_be(:bug) { create(:label, project: project, title: 'Bug') } let_it_be(:bug) { create(:label, project: project, title: 'Bug') }
let(:service) { described_class.new(project, developer) } let(:milestone) { create(:milestone, project: project, title: '9.10') }
let(:commit) { create(:commit, project: project) }
subject(:service) { described_class.new(project, developer) }
before_all do before_all do
public_project.add_developer(developer) public_project.add_developer(developer)
...@@ -970,6 +971,32 @@ RSpec.describe QuickActions::InterpretService do ...@@ -970,6 +971,32 @@ RSpec.describe QuickActions::InterpretService do
it_behaves_like 'assign_reviewer command' it_behaves_like 'assign_reviewer command'
end end
context 'with a private user' do
let(:ref) { create(:user, :unconfirmed).to_reference }
let(:content) { "/assign_reviewer #{ref}" }
it_behaves_like 'failed command', 'a parse error' do
let(:match_msg) { eq "Could not apply assign_reviewer command. Failed to find users for '#{ref}'." }
end
end
context 'with a private user, bare username' do
let(:ref) { create(:user, :unconfirmed).username }
let(:content) { "/assign_reviewer #{ref}" }
it_behaves_like 'failed command', 'a parse error' do
let(:match_msg) { eq "Could not apply assign_reviewer command. Failed to find users for '#{ref}'." }
end
end
context 'with @all' do
let(:content) { "/assign_reviewer @all" }
it_behaves_like 'failed command', 'a parse error' do
let(:match_msg) { eq "Could not apply assign_reviewer command. Failed to find users for '@all'." }
end
end
context 'with an incorrect user' do context 'with an incorrect user' do
let(:content) { '/assign_reviewer @abcd1234' } let(:content) { '/assign_reviewer @abcd1234' }
...@@ -997,11 +1024,10 @@ RSpec.describe QuickActions::InterpretService do ...@@ -997,11 +1024,10 @@ RSpec.describe QuickActions::InterpretService do
end end
context 'with extra text' do context 'with extra text' do
let(:arg) { "@#{developer.username} do it!" } let(:content) { "/assign_reviewer #{developer.to_reference} do it!" }
let(:content) { "/assign_reviewer #{arg}" }
it_behaves_like 'failed command', 'a parse error' do it_behaves_like 'failed command', 'a parse error' do
let(:match_msg) { eq "Could not apply assign_reviewer command. Failed to find users for '#{arg}'." } let(:match_msg) { eq "Could not apply assign_reviewer command. Failed to find users for 'do' and 'it!'." }
end end
end end
end end
...@@ -2451,7 +2477,8 @@ RSpec.describe QuickActions::InterpretService do ...@@ -2451,7 +2477,8 @@ RSpec.describe QuickActions::InterpretService do
it 'tells us why we cannot do that' do it 'tells us why we cannot do that' do
_, explanations = service.explain(content, merge_request) _, explanations = service.explain(content, merge_request)
expect(explanations).to eq ["Problem with assign command: Failed to find users for '#{arg}'."] expect(explanations)
.to contain_exactly "Problem with assign command: Failed to find users for 'to', 'this', and 'issue'."
end end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment