Commit 578488ee authored by Douwe Maan's avatar Douwe Maan

Merge branch...

Merge branch '22592-can-set-due-date-through-slash-commands-even-though-i-m-not-authorized-to' into 'master'

Fix permission for setting an issue's due date

## What does this MR do?

This merge request ensure the current user can `:admin_issue` in order to change the issue's `due_date`, in `BaseIssuableService` and in `SlashCommands::InterpretService`.

Closes #22592 

## Are there points in the code the reviewer needs to double check?

No.

## Does this MR meet the acceptance criteria?

- [x] [CHANGELOG](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CHANGELOG) entry added
- Tests
  - [x] Added for this feature/bug
  - [ ] All builds are passing
- [x] Conform by the [merge request performance guides](http://docs.gitlab.com/ce/development/merge_request_performance_guidelines.html)
- [ ] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#style-guides)
- [x] Branch has no merge conflicts with `master` (if you do - rebase it please)
- [x] [Squashed related commits together](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)

See merge request !6539
parents 51fcea7a adab5dba
...@@ -7,6 +7,7 @@ v 8.13.0 (unreleased) ...@@ -7,6 +7,7 @@ v 8.13.0 (unreleased)
- Log LDAP lookup errors and don't swallow unrelated exceptions. !6103 (Markus Koller) - Log LDAP lookup errors and don't swallow unrelated exceptions. !6103 (Markus Koller)
- Add more tests for calendar contribution (ClemMakesApps) - Add more tests for calendar contribution (ClemMakesApps)
- Avoid database queries on Banzai::ReferenceParser::BaseParser for nodes without references - Avoid database queries on Banzai::ReferenceParser::BaseParser for nodes without references
- Fix permission for setting an issue's due date
- Fix robots.txt disallowing access to groups starting with "s" (Matt Harrison) - Fix robots.txt disallowing access to groups starting with "s" (Matt Harrison)
- Only update issuable labels if they have been changed - Only update issuable labels if they have been changed
- Revoke button in Applications Settings underlines on hover. - Revoke button in Applications Settings underlines on hover.
......
...@@ -50,6 +50,7 @@ class IssuableBaseService < BaseService ...@@ -50,6 +50,7 @@ class IssuableBaseService < BaseService
params.delete(:remove_label_ids) params.delete(:remove_label_ids)
params.delete(:label_ids) params.delete(:label_ids)
params.delete(:assignee_id) params.delete(:assignee_id)
params.delete(:due_date)
end end
end end
......
...@@ -195,7 +195,7 @@ module SlashCommands ...@@ -195,7 +195,7 @@ module SlashCommands
params '<in 2 days | this Friday | December 31st>' params '<in 2 days | this Friday | December 31st>'
condition do condition do
issuable.respond_to?(:due_date) && issuable.respond_to?(:due_date) &&
current_user.can?(:"update_#{issuable.to_ability_name}", issuable) current_user.can?(:"admin_#{issuable.to_ability_name}", project)
end end
command :due do |due_date_param| command :due do |due_date_param|
due_date = Chronic.parse(due_date_param).try(:to_date) due_date = Chronic.parse(due_date_param).try(:to_date)
...@@ -208,7 +208,7 @@ module SlashCommands ...@@ -208,7 +208,7 @@ module SlashCommands
issuable.persisted? && issuable.persisted? &&
issuable.respond_to?(:due_date) && issuable.respond_to?(:due_date) &&
issuable.due_date? && issuable.due_date? &&
current_user.can?(:"update_#{issuable.to_ability_name}", issuable) current_user.can?(:"admin_#{issuable.to_ability_name}", project)
end end
command :remove_due_date do command :remove_due_date do
@updates[:due_date] = nil @updates[:due_date] = nil
......
...@@ -25,6 +25,7 @@ feature 'Issues > User uses slash commands', feature: true, js: true do ...@@ -25,6 +25,7 @@ feature 'Issues > User uses slash commands', feature: true, js: true do
describe 'adding a due date from note' do describe 'adding a due date from note' do
let(:issue) { create(:issue, project: project) } let(:issue) { create(:issue, project: project) }
context 'when the current user can update the due date' do
it 'does not create a note, and sets the due date accordingly' do it 'does not create a note, and sets the due date accordingly' do
write_note("/due 2016-08-28") write_note("/due 2016-08-28")
...@@ -37,9 +38,32 @@ feature 'Issues > User uses slash commands', feature: true, js: true do ...@@ -37,9 +38,32 @@ feature 'Issues > User uses slash commands', feature: true, js: true do
end end
end end
context 'when the current user cannot update the due date' do
let(:guest) { create(:user) }
before do
project.team << [guest, :guest]
logout
login_with(guest)
visit namespace_project_issue_path(project.namespace, project, issue)
end
it 'does not create a note, and sets the due date accordingly' do
write_note("/due 2016-08-28")
expect(page).to have_content '/due 2016-08-28'
expect(page).not_to have_content 'Your commands have been executed!'
issue.reload
expect(issue.due_date).to be_nil
end
end
end
describe 'removing a due date from note' do describe 'removing a due date from note' do
let(:issue) { create(:issue, project: project, due_date: Date.new(2016, 8, 28)) } let(:issue) { create(:issue, project: project, due_date: Date.new(2016, 8, 28)) }
context 'when the current user can update the due date' do
it 'does not create a note, and removes the due date accordingly' do it 'does not create a note, and removes the due date accordingly' do
expect(issue.due_date).to eq Date.new(2016, 8, 28) expect(issue.due_date).to eq Date.new(2016, 8, 28)
...@@ -53,5 +77,27 @@ feature 'Issues > User uses slash commands', feature: true, js: true do ...@@ -53,5 +77,27 @@ feature 'Issues > User uses slash commands', feature: true, js: true do
expect(issue.due_date).to be_nil expect(issue.due_date).to be_nil
end end
end end
context 'when the current user cannot update the due date' do
let(:guest) { create(:user) }
before do
project.team << [guest, :guest]
logout
login_with(guest)
visit namespace_project_issue_path(project.namespace, project, issue)
end
it 'does not create a note, and sets the due date accordingly' do
write_note("/remove_due_date")
expect(page).to have_content '/remove_due_date'
expect(page).not_to have_content 'Your commands have been executed!'
issue.reload
expect(issue.due_date).to eq Date.new(2016, 8, 28)
end
end
end
end end
end end
...@@ -20,16 +20,38 @@ describe Issues::CreateService, services: true do ...@@ -20,16 +20,38 @@ describe Issues::CreateService, services: true do
let(:opts) do let(:opts) do
{ title: 'Awesome issue', { title: 'Awesome issue',
description: 'please fix', description: 'please fix',
assignee: assignee, assignee_id: assignee.id,
label_ids: labels.map(&:id), label_ids: labels.map(&:id),
milestone_id: milestone.id } milestone_id: milestone.id,
due_date: Date.tomorrow }
end
it 'creates the issue with the given params' do
expect(issue).to be_persisted
expect(issue.title).to eq('Awesome issue')
expect(issue.assignee).to eq assignee
expect(issue.labels).to match_array labels
expect(issue.milestone).to eq milestone
expect(issue.due_date).to eq Date.tomorrow
end end
it { expect(issue).to be_valid } context 'when current user cannot admin issues in the project' do
it { expect(issue.title).to eq('Awesome issue') } let(:guest) { create(:user) }
it { expect(issue.assignee).to eq assignee } before do
it { expect(issue.labels).to match_array labels } project.team << [guest, :guest]
it { expect(issue.milestone).to eq milestone } end
it 'filters out params that cannot be set without the :admin_issue permission' do
issue = described_class.new(project, guest, opts).execute
expect(issue).to be_persisted
expect(issue.title).to eq('Awesome issue')
expect(issue.assignee).to be_nil
expect(issue.labels).to be_empty
expect(issue.milestone).to be_nil
expect(issue.due_date).to be_nil
end
end
it 'creates a pending todo for new assignee' do it 'creates a pending todo for new assignee' do
attributes = { attributes = {
......
...@@ -32,27 +32,55 @@ describe Issues::UpdateService, services: true do ...@@ -32,27 +32,55 @@ describe Issues::UpdateService, services: true do
described_class.new(project, user, opts).execute(issue) described_class.new(project, user, opts).execute(issue)
end end
context "valid params" do context 'valid params' do
before do let(:opts) do
opts = { {
title: 'New title', title: 'New title',
description: 'Also please fix', description: 'Also please fix',
assignee_id: user2.id, assignee_id: user2.id,
state_event: 'close', state_event: 'close',
label_ids: [label.id] label_ids: [label.id],
due_date: Date.tomorrow
} }
end
perform_enqueued_jobs do it 'updates the issue with the given params' do
update_issue(opts) update_issue(opts)
expect(issue).to be_valid
expect(issue.title).to eq 'New title'
expect(issue.description).to eq 'Also please fix'
expect(issue.assignee).to eq user2
expect(issue).to be_closed
expect(issue.labels).to match_array [label]
expect(issue.due_date).to eq Date.tomorrow
end end
context 'when current user cannot admin issues in the project' do
let(:guest) { create(:user) }
before do
project.team << [guest, :guest]
end end
it { expect(issue).to be_valid } it 'filters out params that cannot be set without the :admin_issue permission' do
it { expect(issue.title).to eq('New title') } described_class.new(project, guest, opts).execute(issue)
it { expect(issue.assignee).to eq(user2) }
it { expect(issue).to be_closed } expect(issue).to be_valid
it { expect(issue.labels.count).to eq(1) } expect(issue.title).to eq 'New title'
it { expect(issue.labels.first.title).to eq(label.name) } expect(issue.description).to eq 'Also please fix'
expect(issue.assignee).to eq user3
expect(issue.labels).to be_empty
expect(issue.milestone).to be_nil
expect(issue.due_date).to be_nil
end
end
context 'with background jobs processed' do
before do
perform_enqueued_jobs do
update_issue(opts)
end
end
it 'sends email to user2 about assign of new issue and email to user3 about issue unassignment' do it 'sends email to user2 about assign of new issue and email to user3 about issue unassignment' do
deliveries = ActionMailer::Base.deliveries deliveries = ActionMailer::Base.deliveries
...@@ -83,6 +111,7 @@ describe Issues::UpdateService, services: true do ...@@ -83,6 +111,7 @@ describe Issues::UpdateService, services: true do
expect(note.note).to eq 'Changed title: **{-Old-} title** → **{+New+} title**' expect(note.note).to eq 'Changed title: **{-Old-} title** → **{+New+} title**'
end end
end end
end
context 'when issue turns confidential' do context 'when issue turns confidential' do
let(:opts) do let(:opts) do
......
require 'spec_helper' require 'spec_helper'
describe SlashCommands::InterpretService, services: true do describe SlashCommands::InterpretService, services: true do
let(:project) { create(:project) } let(:project) { create(:empty_project, :public) }
let(:user) { create(:user) } let(:developer) { create(:user) }
let(:issue) { create(:issue, project: project) } let(:issue) { create(:issue, project: project) }
let(:milestone) { create(:milestone, project: project, title: '9.10') } let(:milestone) { create(:milestone, project: project, title: '9.10') }
let(:inprogress) { create(:label, project: project, title: 'In Progress') } let(:inprogress) { create(:label, project: project, title: 'In Progress') }
let(:bug) { create(:label, project: project, title: 'Bug') } let(:bug) { create(:label, project: project, title: 'Bug') }
before do before do
project.team << [user, :developer] project.team << [developer, :developer]
end end
describe '#execute' do describe '#execute' do
let(:service) { described_class.new(project, user) } let(:service) { described_class.new(project, developer) }
let(:merge_request) { create(:merge_request, source_project: project) } let(:merge_request) { create(:merge_request, source_project: project) }
shared_examples 'reopen command' do shared_examples 'reopen command' do
...@@ -45,13 +45,13 @@ describe SlashCommands::InterpretService, services: true do ...@@ -45,13 +45,13 @@ describe SlashCommands::InterpretService, services: true do
it 'fetches assignee and populates assignee_id if content contains /assign' do it 'fetches assignee and populates assignee_id if content contains /assign' do
_, updates = service.execute(content, issuable) _, updates = service.execute(content, issuable)
expect(updates).to eq(assignee_id: user.id) expect(updates).to eq(assignee_id: developer.id)
end end
end end
shared_examples 'unassign command' do shared_examples 'unassign command' do
it 'populates assignee_id: nil if content contains /unassign' do it 'populates assignee_id: nil if content contains /unassign' do
issuable.update(assignee_id: user.id) issuable.update(assignee_id: developer.id)
_, updates = service.execute(content, issuable) _, updates = service.execute(content, issuable)
expect(updates).to eq(assignee_id: nil) expect(updates).to eq(assignee_id: nil)
...@@ -124,7 +124,7 @@ describe SlashCommands::InterpretService, services: true do ...@@ -124,7 +124,7 @@ describe SlashCommands::InterpretService, services: true do
shared_examples 'done command' do shared_examples 'done command' do
it 'populates todo_event: "done" if content contains /done' do it 'populates todo_event: "done" if content contains /done' do
TodoService.new.mark_todo(issuable, user) TodoService.new.mark_todo(issuable, developer)
_, updates = service.execute(content, issuable) _, updates = service.execute(content, issuable)
expect(updates).to eq(todo_event: 'done') expect(updates).to eq(todo_event: 'done')
...@@ -141,7 +141,7 @@ describe SlashCommands::InterpretService, services: true do ...@@ -141,7 +141,7 @@ describe SlashCommands::InterpretService, services: true do
shared_examples 'unsubscribe command' do shared_examples 'unsubscribe command' do
it 'populates subscription_event: "unsubscribe" if content contains /unsubscribe' do it 'populates subscription_event: "unsubscribe" if content contains /unsubscribe' do
issuable.subscribe(user) issuable.subscribe(developer)
_, updates = service.execute(content, issuable) _, updates = service.execute(content, issuable)
expect(updates).to eq(subscription_event: 'unsubscribe') expect(updates).to eq(subscription_event: 'unsubscribe')
...@@ -209,12 +209,12 @@ describe SlashCommands::InterpretService, services: true do ...@@ -209,12 +209,12 @@ describe SlashCommands::InterpretService, services: true do
end end
it_behaves_like 'assign command' do it_behaves_like 'assign command' do
let(:content) { "/assign @#{user.username}" } let(:content) { "/assign @#{developer.username}" }
let(:issuable) { issue } let(:issuable) { issue }
end end
it_behaves_like 'assign command' do it_behaves_like 'assign command' do
let(:content) { "/assign @#{user.username}" } let(:content) { "/assign @#{developer.username}" }
let(:issuable) { merge_request } let(:issuable) { merge_request }
end end
...@@ -380,5 +380,56 @@ describe SlashCommands::InterpretService, services: true do ...@@ -380,5 +380,56 @@ describe SlashCommands::InterpretService, services: true do
let(:content) { '/remove_due_date' } let(:content) { '/remove_due_date' }
let(:issuable) { merge_request } let(:issuable) { merge_request }
end end
context 'when current_user cannot :admin_issue' do
let(:visitor) { create(:user) }
let(:issue) { create(:issue, project: project, author: visitor) }
let(:service) { described_class.new(project, visitor) }
it_behaves_like 'empty command' do
let(:content) { "/assign @#{developer.username}" }
let(:issuable) { issue }
end
it_behaves_like 'empty command' do
let(:content) { '/unassign' }
let(:issuable) { issue }
end
it_behaves_like 'empty command' do
let(:content) { "/milestone %#{milestone.title}" }
let(:issuable) { issue }
end
it_behaves_like 'empty command' do
let(:content) { '/remove_milestone' }
let(:issuable) { issue }
end
it_behaves_like 'empty command' do
let(:content) { %(/label ~"#{inprogress.title}" ~#{bug.title} ~unknown) }
let(:issuable) { issue }
end
it_behaves_like 'empty command' do
let(:content) { %(/unlabel ~"#{inprogress.title}") }
let(:issuable) { issue }
end
it_behaves_like 'empty command' do
let(:content) { %(/relabel ~"#{inprogress.title}") }
let(:issuable) { issue }
end
it_behaves_like 'empty command' do
let(:content) { '/due tomorrow' }
let(:issuable) { issue }
end
it_behaves_like 'empty command' do
let(:content) { '/remove_due_date' }
let(:issuable) { 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