Commit 0c0ef7df authored by Douwe Maan's avatar Douwe Maan

Merge branch 'confidential-issues-in-private-projects' into 'master'

Allow users to create confidential issues in private projects

Closes #14787

## What does this MR do?

Allow users to create confidential issues in private projects, and exclude access to them to project members with `Guest` role.

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

The query generated by the `User#authorized_projects` method.

## Why was this MR needed?

Community have been requesting this feature.

## What are the relevant issue numbers?

https://gitlab.com/gitlab-org/gitlab-ce/issues/14787

https://gitlab.com/gitlab-org/gitlab-ce/issues/3678

## Screenshots (if relevant)

Not relevant.

## Todo

- [x] Allow users to create confidential issues in private projects
- [x] Project members with `Guest` role should not have access to confidential issues
- [ ] ~~Apply changes in EE + Elasticsearch~~ Will be done in another MR, when this got merged

See merge request !3471
parents 0068ba8d 6789d2eb
...@@ -76,6 +76,7 @@ v 8.9.0 (unreleased) ...@@ -76,6 +76,7 @@ v 8.9.0 (unreleased)
- Put project Labels and Milestones pages links under Issues and Merge Requests tabs as subnav - Put project Labels and Milestones pages links under Issues and Merge Requests tabs as subnav
- All classes in the Banzai::ReferenceParser namespace are now instrumented - All classes in the Banzai::ReferenceParser namespace are now instrumented
- Remove deprecated issues_tracker and issues_tracker_id from project model - Remove deprecated issues_tracker and issues_tracker_id from project model
- Allow users to create confidential issues in private projects
v 8.8.5 (unreleased) v 8.8.5 (unreleased)
- Ensure branch cleanup regardless of whether the GitHub import process succeeds - Ensure branch cleanup regardless of whether the GitHub import process succeeds
......
...@@ -51,7 +51,7 @@ class SnippetsFinder ...@@ -51,7 +51,7 @@ class SnippetsFinder
snippets = project.snippets.fresh snippets = project.snippets.fresh
if current_user if current_user
if project.team.member?(current_user.id) || current_user.admin? if project.team.member?(current_user) || current_user.admin?
snippets snippets
else else
snippets.public_and_internal snippets.public_and_internal
......
...@@ -533,7 +533,7 @@ class Ability ...@@ -533,7 +533,7 @@ class Ability
def filter_confidential_issues_abilities(user, issue, rules) def filter_confidential_issues_abilities(user, issue, rules)
return rules if user.admin? || !issue.confidential? return rules if user.admin? || !issue.confidential?
unless issue.author == user || issue.assignee == user || issue.project.team.member?(user.id) unless issue.author == user || issue.assignee == user || issue.project.team.member?(user, Gitlab::Access::REPORTER)
rules.delete(:admin_issue) rules.delete(:admin_issue)
rules.delete(:read_issue) rules.delete(:read_issue)
rules.delete(:update_issue) rules.delete(:update_issue)
......
...@@ -51,10 +51,18 @@ class Issue < ActiveRecord::Base ...@@ -51,10 +51,18 @@ class Issue < ActiveRecord::Base
end end
def self.visible_to_user(user) def self.visible_to_user(user)
return where(confidential: false) if user.blank? return where('issues.confidential IS NULL OR issues.confidential IS FALSE') if user.blank?
return all if user.admin? return all if user.admin?
where('issues.confidential = false OR (issues.confidential = true AND (issues.author_id = :user_id OR issues.assignee_id = :user_id OR issues.project_id IN(:project_ids)))', user_id: user.id, project_ids: user.authorized_projects.select(:id)) where('
issues.confidential IS NULL
OR issues.confidential IS FALSE
OR (issues.confidential = TRUE
AND (issues.author_id = :user_id
OR issues.assignee_id = :user_id
OR issues.project_id IN(:project_ids)))',
user_id: user.id,
project_ids: user.authorized_projects(Gitlab::Access::REPORTER).select(:id))
end end
def self.reference_prefix def self.reference_prefix
......
...@@ -88,22 +88,9 @@ class Note < ActiveRecord::Base ...@@ -88,22 +88,9 @@ class Note < ActiveRecord::Base
table = arel_table table = arel_table
pattern = "%#{query}%" pattern = "%#{query}%"
found_notes = joins('LEFT JOIN issues ON issues.id = noteable_id'). Note.joins('LEFT JOIN issues ON issues.id = noteable_id').
where(table[:note].matches(pattern)) where(table[:note].matches(pattern)).
merge(Issue.visible_to_user(as_user))
if as_user
found_notes.where('
issues.confidential IS NULL
OR issues.confidential IS FALSE
OR (issues.confidential IS TRUE
AND (issues.author_id = :user_id
OR issues.assignee_id = :user_id
OR issues.project_id IN(:project_ids)))',
user_id: as_user.id,
project_ids: as_user.authorized_projects.select(:id))
else
found_notes.where('issues.confidential IS NULL OR issues.confidential IS FALSE')
end
end end
end end
......
...@@ -131,8 +131,14 @@ class ProjectTeam ...@@ -131,8 +131,14 @@ class ProjectTeam
max_member_access(user.id) == Gitlab::Access::MASTER max_member_access(user.id) == Gitlab::Access::MASTER
end end
def member?(user_id) def member?(user, min_member_access = nil)
!!find_member(user_id) member = !!find_member(user.id)
if min_member_access
member && max_member_access(user.id) >= min_member_access
else
member
end
end end
def human_max_access(user_id) def human_max_access(user_id)
......
...@@ -405,8 +405,8 @@ class User < ActiveRecord::Base ...@@ -405,8 +405,8 @@ class User < ActiveRecord::Base
end end
# Returns projects user is authorized to access. # Returns projects user is authorized to access.
def authorized_projects def authorized_projects(min_access_level = nil)
Project.where("projects.id IN (#{projects_union.to_sql})") Project.where("projects.id IN (#{projects_union(min_access_level).to_sql})")
end end
def viewable_starred_projects def viewable_starred_projects
...@@ -824,11 +824,19 @@ class User < ActiveRecord::Base ...@@ -824,11 +824,19 @@ class User < ActiveRecord::Base
private private
def projects_union def projects_union(min_access_level = nil)
Gitlab::SQL::Union.new([personal_projects.select(:id), relations = [personal_projects.select(:id),
groups_projects.select(:id), groups_projects.select(:id),
projects.select(:id), projects.select(:id),
groups.joins(:shared_projects).select(:project_id)]) groups.joins(:shared_projects).select(:project_id)]
if min_access_level
scope = { access_level: Gitlab::Access.values.select { |access| access >= min_access_level } }
relations = [relations.shift] + relations.map { |relation| relation.where(members: scope) }
end
Gitlab::SQL::Union.new(relations)
end end
def ci_projects_union def ci_projects_union
......
...@@ -35,13 +35,13 @@ ...@@ -35,13 +35,13 @@
.clearfix .clearfix
.error-alert .error-alert
- if issuable.is_a?(Issue) && !issuable.project.private? - if issuable.is_a?(Issue)
.form-group .form-group
.col-sm-offset-2.col-sm-10 .col-sm-offset-2.col-sm-10
.checkbox .checkbox
= f.label :confidential do = f.label :confidential do
= f.check_box :confidential = f.check_box :confidential
This issue is confidential and should only be visible to team members This issue is confidential and should only be visible to team members with at least Reporter access.
- if can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project) - if can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project)
- has_due_date = issuable.has_attribute?(:due_date) - has_due_date = issuable.has_attribute?(:due_date)
......
...@@ -105,6 +105,15 @@ describe Projects::IssuesController do ...@@ -105,6 +105,15 @@ describe Projects::IssuesController do
expect(assigns(:issues)).to eq [issue] expect(assigns(:issues)).to eq [issue]
end end
it 'should not list confidential issues for project members with guest role' do
sign_in(member)
project.team << [member, :guest]
get_issues
expect(assigns(:issues)).to eq [issue]
end
it 'should list confidential issues for author' do it 'should list confidential issues for author' do
sign_in(author) sign_in(author)
get_issues get_issues
...@@ -148,7 +157,7 @@ describe Projects::IssuesController do ...@@ -148,7 +157,7 @@ describe Projects::IssuesController do
shared_examples_for 'restricted action' do |http_status| shared_examples_for 'restricted action' do |http_status|
it 'returns 404 for guests' do it 'returns 404 for guests' do
sign_out :user sign_out(:user)
go(id: unescaped_parameter_value.to_param) go(id: unescaped_parameter_value.to_param)
expect(response).to have_http_status :not_found expect(response).to have_http_status :not_found
...@@ -161,6 +170,14 @@ describe Projects::IssuesController do ...@@ -161,6 +170,14 @@ describe Projects::IssuesController do
expect(response).to have_http_status :not_found expect(response).to have_http_status :not_found
end end
it 'returns 404 for project members with guest role' do
sign_in(member)
project.team << [member, :guest]
go(id: unescaped_parameter_value.to_param)
expect(response).to have_http_status :not_found
end
it "returns #{http_status[:success]} for author" do it "returns #{http_status[:success]} for author" do
sign_in(author) sign_in(author)
go(id: unescaped_parameter_value.to_param) go(id: unescaped_parameter_value.to_param)
......
...@@ -69,6 +69,18 @@ describe Banzai::Filter::RedactorFilter, lib: true do ...@@ -69,6 +69,18 @@ describe Banzai::Filter::RedactorFilter, lib: true do
expect(doc.css('a').length).to eq 0 expect(doc.css('a').length).to eq 0
end end
it 'removes references for project members with guest role' do
member = create(:user)
project = create(:empty_project, :public)
project.team << [member, :guest]
issue = create(:issue, :confidential, project: project)
link = reference_link(project: project.id, issue: issue.id, reference_type: 'issue')
doc = filter(link, current_user: member)
expect(doc.css('a').length).to eq 0
end
it 'allows references for author' do it 'allows references for author' do
author = create(:user) author = create(:user)
project = create(:empty_project, :public) project = create(:empty_project, :public)
......
...@@ -43,6 +43,18 @@ describe Gitlab::ProjectSearchResults, lib: true do ...@@ -43,6 +43,18 @@ describe Gitlab::ProjectSearchResults, lib: true do
expect(results.issues_count).to eq 1 expect(results.issues_count).to eq 1
end end
it 'should not list project confidential issues for project members with guest role' do
project.team << [member, :guest]
results = described_class.new(member, project, query)
issues = results.objects('issues')
expect(issues).to include issue
expect(issues).not_to include security_issue_1
expect(issues).not_to include security_issue_2
expect(results.issues_count).to eq 1
end
it 'should list project confidential issues for author' do it 'should list project confidential issues for author' do
results = described_class.new(author, project, query) results = described_class.new(author, project, query)
issues = results.objects('issues') issues = results.objects('issues')
......
...@@ -86,6 +86,22 @@ describe Gitlab::SearchResults do ...@@ -86,6 +86,22 @@ describe Gitlab::SearchResults do
expect(results.issues_count).to eq 1 expect(results.issues_count).to eq 1
end end
it 'should not list confidential issues for project members with guest role' do
project_1.team << [member, :guest]
project_2.team << [member, :guest]
results = described_class.new(member, limit_projects, query)
issues = results.objects('issues')
expect(issues).to include issue
expect(issues).not_to include security_issue_1
expect(issues).not_to include security_issue_2
expect(issues).not_to include security_issue_3
expect(issues).not_to include security_issue_4
expect(issues).not_to include security_issue_5
expect(results.issues_count).to eq 1
end
it 'should list confidential issues for author' do it 'should list confidential issues for author' do
results = described_class.new(author, limit_projects, query) results = described_class.new(author, limit_projects, query)
issues = results.objects('issues') issues = results.objects('issues')
......
...@@ -5,6 +5,7 @@ describe Milestone, 'Milestoneish' do ...@@ -5,6 +5,7 @@ describe Milestone, 'Milestoneish' do
let(:assignee) { create(:user) } let(:assignee) { create(:user) }
let(:non_member) { create(:user) } let(:non_member) { create(:user) }
let(:member) { create(:user) } let(:member) { create(:user) }
let(:guest) { create(:user) }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let(:project) { create(:project, :public) } let(:project) { create(:project, :public) }
let(:milestone) { create(:milestone, project: project) } let(:milestone) { create(:milestone, project: project) }
...@@ -21,6 +22,7 @@ describe Milestone, 'Milestoneish' do ...@@ -21,6 +22,7 @@ describe Milestone, 'Milestoneish' do
before do before do
project.team << [member, :developer] project.team << [member, :developer]
project.team << [guest, :guest]
end end
describe '#closed_items_count' do describe '#closed_items_count' do
...@@ -28,6 +30,10 @@ describe Milestone, 'Milestoneish' do ...@@ -28,6 +30,10 @@ describe Milestone, 'Milestoneish' do
expect(milestone.closed_items_count(non_member)).to eq 2 expect(milestone.closed_items_count(non_member)).to eq 2
end end
it 'should not count confidential issues for project members with guest role' do
expect(milestone.closed_items_count(guest)).to eq 2
end
it 'should count confidential issues for author' do it 'should count confidential issues for author' do
expect(milestone.closed_items_count(author)).to eq 4 expect(milestone.closed_items_count(author)).to eq 4
end end
...@@ -50,6 +56,10 @@ describe Milestone, 'Milestoneish' do ...@@ -50,6 +56,10 @@ describe Milestone, 'Milestoneish' do
expect(milestone.total_items_count(non_member)).to eq 4 expect(milestone.total_items_count(non_member)).to eq 4
end end
it 'should not count confidential issues for project members with guest role' do
expect(milestone.total_items_count(guest)).to eq 4
end
it 'should count confidential issues for author' do it 'should count confidential issues for author' do
expect(milestone.total_items_count(author)).to eq 7 expect(milestone.total_items_count(author)).to eq 7
end end
...@@ -85,6 +95,10 @@ describe Milestone, 'Milestoneish' do ...@@ -85,6 +95,10 @@ describe Milestone, 'Milestoneish' do
expect(milestone.percent_complete(non_member)).to eq 50 expect(milestone.percent_complete(non_member)).to eq 50
end end
it 'should not count confidential issues for project members with guest role' do
expect(milestone.percent_complete(guest)).to eq 50
end
it 'should count confidential issues for author' do it 'should count confidential issues for author' do
expect(milestone.percent_complete(author)).to eq 57 expect(milestone.percent_complete(author)).to eq 57
end end
......
...@@ -50,6 +50,7 @@ describe Event, models: true do ...@@ -50,6 +50,7 @@ describe Event, models: true do
let(:project) { create(:empty_project, :public) } let(:project) { create(:empty_project, :public) }
let(:non_member) { create(:user) } let(:non_member) { create(:user) }
let(:member) { create(:user) } let(:member) { create(:user) }
let(:guest) { create(:user) }
let(:author) { create(:author) } let(:author) { create(:author) }
let(:assignee) { create(:user) } let(:assignee) { create(:user) }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
...@@ -61,6 +62,7 @@ describe Event, models: true do ...@@ -61,6 +62,7 @@ describe Event, models: true do
before do before do
project.team << [member, :developer] project.team << [member, :developer]
project.team << [guest, :guest]
end end
context 'issue event' do context 'issue event' do
...@@ -71,6 +73,7 @@ describe Event, models: true do ...@@ -71,6 +73,7 @@ describe Event, models: true do
it { expect(event.visible_to_user?(author)).to eq true } it { expect(event.visible_to_user?(author)).to eq true }
it { expect(event.visible_to_user?(assignee)).to eq true } it { expect(event.visible_to_user?(assignee)).to eq true }
it { expect(event.visible_to_user?(member)).to eq true } it { expect(event.visible_to_user?(member)).to eq true }
it { expect(event.visible_to_user?(guest)).to eq true }
it { expect(event.visible_to_user?(admin)).to eq true } it { expect(event.visible_to_user?(admin)).to eq true }
end end
...@@ -81,6 +84,7 @@ describe Event, models: true do ...@@ -81,6 +84,7 @@ describe Event, models: true do
it { expect(event.visible_to_user?(author)).to eq true } it { expect(event.visible_to_user?(author)).to eq true }
it { expect(event.visible_to_user?(assignee)).to eq true } it { expect(event.visible_to_user?(assignee)).to eq true }
it { expect(event.visible_to_user?(member)).to eq true } it { expect(event.visible_to_user?(member)).to eq true }
it { expect(event.visible_to_user?(guest)).to eq false }
it { expect(event.visible_to_user?(admin)).to eq true } it { expect(event.visible_to_user?(admin)).to eq true }
end end
end end
...@@ -93,6 +97,7 @@ describe Event, models: true do ...@@ -93,6 +97,7 @@ describe Event, models: true do
it { expect(event.visible_to_user?(author)).to eq true } it { expect(event.visible_to_user?(author)).to eq true }
it { expect(event.visible_to_user?(assignee)).to eq true } it { expect(event.visible_to_user?(assignee)).to eq true }
it { expect(event.visible_to_user?(member)).to eq true } it { expect(event.visible_to_user?(member)).to eq true }
it { expect(event.visible_to_user?(guest)).to eq true }
it { expect(event.visible_to_user?(admin)).to eq true } it { expect(event.visible_to_user?(admin)).to eq true }
end end
...@@ -103,6 +108,7 @@ describe Event, models: true do ...@@ -103,6 +108,7 @@ describe Event, models: true do
it { expect(event.visible_to_user?(author)).to eq true } it { expect(event.visible_to_user?(author)).to eq true }
it { expect(event.visible_to_user?(assignee)).to eq true } it { expect(event.visible_to_user?(assignee)).to eq true }
it { expect(event.visible_to_user?(member)).to eq true } it { expect(event.visible_to_user?(member)).to eq true }
it { expect(event.visible_to_user?(guest)).to eq false }
it { expect(event.visible_to_user?(admin)).to eq true } it { expect(event.visible_to_user?(admin)).to eq true }
end end
end end
......
...@@ -162,16 +162,23 @@ describe Note, models: true do ...@@ -162,16 +162,23 @@ describe Note, models: true do
end end
context "confidential issues" do context "confidential issues" do
let(:user) { create :user } let(:user) { create(:user) }
let(:confidential_issue) { create(:issue, :confidential, author: user) } let(:project) { create(:project) }
let(:confidential_note) { create :note, note: "Random", noteable: confidential_issue, project: confidential_issue.project } let(:confidential_issue) { create(:issue, :confidential, project: project, author: user) }
let(:confidential_note) { create(:note, note: "Random", noteable: confidential_issue, project: confidential_issue.project) }
it "returns notes with matching content if user can see the issue" do it "returns notes with matching content if user can see the issue" do
expect(described_class.search(confidential_note.note, as_user: user)).to eq([confidential_note]) expect(described_class.search(confidential_note.note, as_user: user)).to eq([confidential_note])
end end
it "does not return notes with matching content if user can not see the issue" do it "does not return notes with matching content if user can not see the issue" do
user = create :user user = create(:user)
expect(described_class.search(confidential_note.note, as_user: user)).to be_empty
end
it "does not return notes with matching content for project members with guest role" do
user = create(:user)
project.team << [user, :guest]
expect(described_class.search(confidential_note.note, as_user: user)).to be_empty expect(described_class.search(confidential_note.note, as_user: user)).to be_empty
end end
......
...@@ -29,6 +29,9 @@ describe ProjectTeam, models: true do ...@@ -29,6 +29,9 @@ describe ProjectTeam, models: true do
it { expect(project.team.master?(nonmember)).to be_falsey } it { expect(project.team.master?(nonmember)).to be_falsey }
it { expect(project.team.member?(nonmember)).to be_falsey } it { expect(project.team.member?(nonmember)).to be_falsey }
it { expect(project.team.member?(guest)).to be_truthy } it { expect(project.team.member?(guest)).to be_truthy }
it { expect(project.team.member?(reporter, Gitlab::Access::REPORTER)).to be_truthy }
it { expect(project.team.member?(guest, Gitlab::Access::REPORTER)).to be_falsey }
it { expect(project.team.member?(nonmember, Gitlab::Access::GUEST)).to be_falsey }
end end
end end
...@@ -64,6 +67,9 @@ describe ProjectTeam, models: true do ...@@ -64,6 +67,9 @@ describe ProjectTeam, models: true do
it { expect(project.team.master?(nonmember)).to be_falsey } it { expect(project.team.master?(nonmember)).to be_falsey }
it { expect(project.team.member?(nonmember)).to be_falsey } it { expect(project.team.member?(nonmember)).to be_falsey }
it { expect(project.team.member?(guest)).to be_truthy } it { expect(project.team.member?(guest)).to be_truthy }
it { expect(project.team.member?(guest, Gitlab::Access::MASTER)).to be_truthy }
it { expect(project.team.member?(reporter, Gitlab::Access::MASTER)).to be_falsey }
it { expect(project.team.member?(nonmember, Gitlab::Access::GUEST)).to be_falsey }
end end
end end
......
...@@ -5,6 +5,7 @@ describe API::API, api: true do ...@@ -5,6 +5,7 @@ describe API::API, api: true do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:user2) { create(:user) } let(:user2) { create(:user) }
let(:non_member) { create(:user) } let(:non_member) { create(:user) }
let(:guest) { create(:user) }
let(:author) { create(:author) } let(:author) { create(:author) }
let(:assignee) { create(:assignee) } let(:assignee) { create(:assignee) }
let(:admin) { create(:user, :admin) } let(:admin) { create(:user, :admin) }
...@@ -41,7 +42,10 @@ describe API::API, api: true do ...@@ -41,7 +42,10 @@ describe API::API, api: true do
end end
let!(:note) { create(:note_on_issue, author: user, project: project, noteable: issue) } let!(:note) { create(:note_on_issue, author: user, project: project, noteable: issue) }
before { project.team << [user, :reporter] } before do
project.team << [user, :reporter]
project.team << [guest, :guest]
end
describe "GET /issues" do describe "GET /issues" do
context "when unauthenticated" do context "when unauthenticated" do
...@@ -144,6 +148,14 @@ describe API::API, api: true do ...@@ -144,6 +148,14 @@ describe API::API, api: true do
expect(json_response.first['title']).to eq(issue.title) expect(json_response.first['title']).to eq(issue.title)
end end
it 'should return project issues without confidential issues for project members with guest role' do
get api("#{base_url}/issues", guest)
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(2)
expect(json_response.first['title']).to eq(issue.title)
end
it 'should return project confidential issues for author' do it 'should return project confidential issues for author' do
get api("#{base_url}/issues", author) get api("#{base_url}/issues", author)
expect(response.status).to eq(200) expect(response.status).to eq(200)
...@@ -278,6 +290,11 @@ describe API::API, api: true do ...@@ -278,6 +290,11 @@ describe API::API, api: true do
expect(response.status).to eq(404) expect(response.status).to eq(404)
end end
it "should return 404 for project members with guest role" do
get api("/projects/#{project.id}/issues/#{confidential_issue.id}", guest)
expect(response.status).to eq(404)
end
it "should return confidential issue for project members" do it "should return confidential issue for project members" do
get api("/projects/#{project.id}/issues/#{confidential_issue.id}", user) get api("/projects/#{project.id}/issues/#{confidential_issue.id}", user)
expect(response.status).to eq(200) expect(response.status).to eq(200)
...@@ -413,6 +430,12 @@ describe API::API, api: true do ...@@ -413,6 +430,12 @@ describe API::API, api: true do
expect(response.status).to eq(403) expect(response.status).to eq(403)
end end
it "should return 403 for project members with guest role" do
put api("/projects/#{project.id}/issues/#{confidential_issue.id}", guest),
title: 'updated title'
expect(response.status).to eq(403)
end
it "should update a confidential issue for project members" do it "should update a confidential issue for project members" do
put api("/projects/#{project.id}/issues/#{confidential_issue.id}", user), put api("/projects/#{project.id}/issues/#{confidential_issue.id}", user),
title: 'updated title' title: 'updated title'
......
...@@ -146,6 +146,7 @@ describe API::API, api: true do ...@@ -146,6 +146,7 @@ describe API::API, api: true do
let(:milestone) { create(:milestone, project: public_project) } let(:milestone) { create(:milestone, project: public_project) }
let(:issue) { create(:issue, project: public_project) } let(:issue) { create(:issue, project: public_project) }
let(:confidential_issue) { create(:issue, confidential: true, project: public_project) } let(:confidential_issue) { create(:issue, confidential: true, project: public_project) }
before do before do
public_project.team << [user, :developer] public_project.team << [user, :developer]
milestone.issues << issue << confidential_issue milestone.issues << issue << confidential_issue
...@@ -160,6 +161,18 @@ describe API::API, api: true do ...@@ -160,6 +161,18 @@ describe API::API, api: true do
expect(json_response.map { |issue| issue['id'] }).to include(issue.id, confidential_issue.id) expect(json_response.map { |issue| issue['id'] }).to include(issue.id, confidential_issue.id)
end end
it 'does not return confidential issues to team members with guest role' do
member = create(:user)
project.team << [member, :guest]
get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", member)
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response.size).to eq(1)
expect(json_response.map { |issue| issue['id'] }).to include(issue.id)
end
it 'does not return confidential issues to regular users' do it 'does not return confidential issues to regular users' do
get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", create(:user)) get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", create(:user))
......
...@@ -132,12 +132,14 @@ describe NotificationService, services: true do ...@@ -132,12 +132,14 @@ describe NotificationService, services: true do
let(:assignee) { create(:user) } let(:assignee) { create(:user) }
let(:non_member) { create(:user) } let(:non_member) { create(:user) }
let(:member) { create(:user) } let(:member) { create(:user) }
let(:guest) { create(:user) }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let(:confidential_issue) { create(:issue, :confidential, project: project, author: author, assignee: assignee) } let(:confidential_issue) { create(:issue, :confidential, project: project, author: author, assignee: assignee) }
let(:note) { create(:note_on_issue, noteable: confidential_issue, project: project, note: "#{author.to_reference} #{assignee.to_reference} #{non_member.to_reference} #{member.to_reference} #{admin.to_reference}") } let(:note) { create(:note_on_issue, noteable: confidential_issue, project: project, note: "#{author.to_reference} #{assignee.to_reference} #{non_member.to_reference} #{member.to_reference} #{admin.to_reference}") }
it 'filters out users that can not read the issue' do it 'filters out users that can not read the issue' do
project.team << [member, :developer] project.team << [member, :developer]
project.team << [guest, :guest]
expect(SentNotification).to receive(:record).with(confidential_issue, any_args).exactly(4).times expect(SentNotification).to receive(:record).with(confidential_issue, any_args).exactly(4).times
...@@ -146,6 +148,7 @@ describe NotificationService, services: true do ...@@ -146,6 +148,7 @@ describe NotificationService, services: true do
notification.new_note(note) notification.new_note(note)
should_not_email(non_member) should_not_email(non_member)
should_not_email(guest)
should_email(author) should_email(author)
should_email(assignee) should_email(assignee)
should_email(member) should_email(member)
...@@ -322,17 +325,20 @@ describe NotificationService, services: true do ...@@ -322,17 +325,20 @@ describe NotificationService, services: true do
let(:assignee) { create(:user) } let(:assignee) { create(:user) }
let(:non_member) { create(:user) } let(:non_member) { create(:user) }
let(:member) { create(:user) } let(:member) { create(:user) }
let(:guest) { create(:user) }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let(:confidential_issue) { create(:issue, :confidential, project: project, title: 'Confidential issue', author: author, assignee: assignee) } let(:confidential_issue) { create(:issue, :confidential, project: project, title: 'Confidential issue', author: author, assignee: assignee) }
it "emails subscribers of the issue's labels that can read the issue" do it "emails subscribers of the issue's labels that can read the issue" do
project.team << [member, :developer] project.team << [member, :developer]
project.team << [guest, :guest]
label = create(:label, issues: [confidential_issue]) label = create(:label, issues: [confidential_issue])
label.toggle_subscription(non_member) label.toggle_subscription(non_member)
label.toggle_subscription(author) label.toggle_subscription(author)
label.toggle_subscription(assignee) label.toggle_subscription(assignee)
label.toggle_subscription(member) label.toggle_subscription(member)
label.toggle_subscription(guest)
label.toggle_subscription(admin) label.toggle_subscription(admin)
ActionMailer::Base.deliveries.clear ActionMailer::Base.deliveries.clear
...@@ -341,6 +347,7 @@ describe NotificationService, services: true do ...@@ -341,6 +347,7 @@ describe NotificationService, services: true do
should_not_email(non_member) should_not_email(non_member)
should_not_email(author) should_not_email(author)
should_not_email(guest)
should_email(assignee) should_email(assignee)
should_email(member) should_email(member)
should_email(admin) should_email(admin)
...@@ -490,6 +497,7 @@ describe NotificationService, services: true do ...@@ -490,6 +497,7 @@ describe NotificationService, services: true do
let(:assignee) { create(:user) } let(:assignee) { create(:user) }
let(:non_member) { create(:user) } let(:non_member) { create(:user) }
let(:member) { create(:user) } let(:member) { create(:user) }
let(:guest) { create(:user) }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let(:confidential_issue) { create(:issue, :confidential, project: project, title: 'Confidential issue', author: author, assignee: assignee) } let(:confidential_issue) { create(:issue, :confidential, project: project, title: 'Confidential issue', author: author, assignee: assignee) }
let!(:label_1) { create(:label, issues: [confidential_issue]) } let!(:label_1) { create(:label, issues: [confidential_issue]) }
...@@ -497,11 +505,13 @@ describe NotificationService, services: true do ...@@ -497,11 +505,13 @@ describe NotificationService, services: true do
it "emails subscribers of the issue's labels that can read the issue" do it "emails subscribers of the issue's labels that can read the issue" do
project.team << [member, :developer] project.team << [member, :developer]
project.team << [guest, :guest]
label_2.toggle_subscription(non_member) label_2.toggle_subscription(non_member)
label_2.toggle_subscription(author) label_2.toggle_subscription(author)
label_2.toggle_subscription(assignee) label_2.toggle_subscription(assignee)
label_2.toggle_subscription(member) label_2.toggle_subscription(member)
label_2.toggle_subscription(guest)
label_2.toggle_subscription(admin) label_2.toggle_subscription(admin)
ActionMailer::Base.deliveries.clear ActionMailer::Base.deliveries.clear
...@@ -509,6 +519,7 @@ describe NotificationService, services: true do ...@@ -509,6 +519,7 @@ describe NotificationService, services: true do
notification.relabeled_issue(confidential_issue, [label_2], @u_disabled) notification.relabeled_issue(confidential_issue, [label_2], @u_disabled)
should_not_email(non_member) should_not_email(non_member)
should_not_email(guest)
should_email(author) should_email(author)
should_email(assignee) should_email(assignee)
should_email(member) should_email(member)
......
...@@ -33,6 +33,18 @@ describe Projects::AutocompleteService, services: true do ...@@ -33,6 +33,18 @@ describe Projects::AutocompleteService, services: true do
expect(issues.count).to eq 1 expect(issues.count).to eq 1
end end
it 'should not list project confidential issues for project members with guest role' do
project.team << [member, :guest]
autocomplete = described_class.new(project, non_member)
issues = autocomplete.issues.map(&:iid)
expect(issues).to include issue.iid
expect(issues).not_to include security_issue_1.iid
expect(issues).not_to include security_issue_2.iid
expect(issues.count).to eq 1
end
it 'should list project confidential issues for author' do it 'should list project confidential issues for author' do
autocomplete = described_class.new(project, author) autocomplete = described_class.new(project, author)
issues = autocomplete.issues.map(&:iid) issues = autocomplete.issues.map(&:iid)
......
...@@ -5,13 +5,15 @@ describe TodoService, services: true do ...@@ -5,13 +5,15 @@ describe TodoService, services: true do
let(:assignee) { create(:user) } let(:assignee) { create(:user) }
let(:non_member) { create(:user) } let(:non_member) { create(:user) }
let(:member) { create(:user) } let(:member) { create(:user) }
let(:guest) { create(:user) }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let(:john_doe) { create(:user) } let(:john_doe) { create(:user) }
let(:project) { create(:project) } let(:project) { create(:project) }
let(:mentions) { [author, assignee, john_doe, member, non_member, admin].map(&:to_reference).join(' ') } let(:mentions) { [author, assignee, john_doe, member, guest, non_member, admin].map(&:to_reference).join(' ') }
let(:service) { described_class.new } let(:service) { described_class.new }
before do before do
project.team << [guest, :guest]
project.team << [author, :developer] project.team << [author, :developer]
project.team << [member, :developer] project.team << [member, :developer]
project.team << [john_doe, :developer] project.team << [john_doe, :developer]
...@@ -41,18 +43,20 @@ describe TodoService, services: true do ...@@ -41,18 +43,20 @@ describe TodoService, services: true do
service.new_issue(issue, author) service.new_issue(issue, author)
should_create_todo(user: member, target: issue, action: Todo::MENTIONED) should_create_todo(user: member, target: issue, action: Todo::MENTIONED)
should_create_todo(user: guest, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED) should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED) should_not_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED) should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
end end
it 'does not create todo for non project members when issue is confidential' do it 'does not create todo if user can not see the issue when issue is confidential' do
service.new_issue(confidential_issue, john_doe) service.new_issue(confidential_issue, john_doe)
should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::ASSIGNED) should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::ASSIGNED)
should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
end end
...@@ -81,6 +85,7 @@ describe TodoService, services: true do ...@@ -81,6 +85,7 @@ describe TodoService, services: true do
service.update_issue(issue, author) service.update_issue(issue, author)
should_create_todo(user: member, target: issue, action: Todo::MENTIONED) should_create_todo(user: member, target: issue, action: Todo::MENTIONED)
should_create_todo(user: guest, target: issue, action: Todo::MENTIONED)
should_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED) should_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED) should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED) should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
...@@ -92,13 +97,14 @@ describe TodoService, services: true do ...@@ -92,13 +97,14 @@ describe TodoService, services: true do
expect { service.update_issue(issue, author) }.not_to change(member.todos, :count) expect { service.update_issue(issue, author) }.not_to change(member.todos, :count)
end end
it 'does not create todo for non project members when issue is confidential' do it 'does not create todo if user can not see the issue when issue is confidential' do
service.update_issue(confidential_issue, john_doe) service.update_issue(confidential_issue, john_doe)
should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
end end
...@@ -192,18 +198,20 @@ describe TodoService, services: true do ...@@ -192,18 +198,20 @@ describe TodoService, services: true do
service.new_note(note, john_doe) service.new_note(note, john_doe)
should_create_todo(user: member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note) should_create_todo(user: member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
should_create_todo(user: guest, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
should_create_todo(user: author, target: issue, author: john_doe, action: Todo::MENTIONED, note: note) should_create_todo(user: author, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
should_not_create_todo(user: john_doe, target: issue, author: john_doe, action: Todo::MENTIONED, note: note) should_not_create_todo(user: john_doe, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
should_not_create_todo(user: non_member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note) should_not_create_todo(user: non_member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
end end
it 'does not create todo for non project members when leaving a note on a confidential issue' do it 'does not create todo if user can not see the issue when leaving a note on a confidential issue' do
service.new_note(note_on_confidential_issue, john_doe) service.new_note(note_on_confidential_issue, john_doe)
should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue) should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue) should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue) should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue) should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue) should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
end end
...@@ -245,6 +253,7 @@ describe TodoService, services: true do ...@@ -245,6 +253,7 @@ describe TodoService, services: true do
service.new_merge_request(mr_assigned, author) service.new_merge_request(mr_assigned, author)
should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED) should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED)
should_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED) should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED) should_not_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED) should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
...@@ -256,6 +265,7 @@ describe TodoService, services: true do ...@@ -256,6 +265,7 @@ describe TodoService, services: true do
service.update_merge_request(mr_assigned, author) service.update_merge_request(mr_assigned, author)
should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED) should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED)
should_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED)
should_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED) should_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED) should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED) should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
......
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