...@@ -516,13 +516,11 @@ class Project < ApplicationRecord ...@@ -516,13 +516,11 @@ class Project < ApplicationRecord
# This scope returns projects where user has access to both the project and the feature. # This scope returns projects where user has access to both the project and the feature.
def self.filter_by_feature_visibility(feature, user) def self.filter_by_feature_visibility(feature, user)
scope = with_feature_available_for_user(feature, user) with_feature_available_for_user(feature, user)
.public_or_visible_to_user(
if ProjectFeature.guest_allowed_on_private_project?(feature) user,
scope.public_or_visible_to_user(user) ProjectFeature.required_minimum_access_level_for_private_project(feature)
else )
scope.public_or_visible_to_user(user, Gitlab::Access::REPORTER)
end
end end
scope :active, -> { joins(:issues, :notes, :merge_requests).order('issues.created_at, notes.created_at, merge_requests.created_at DESC') } scope :active, -> { joins(:issues, :notes, :merge_requests).order('issues.created_at, notes.created_at, merge_requests.created_at DESC') }
... ...
......
...@@ -24,7 +24,7 @@ class ProjectFeature < ApplicationRecord ...@@ -24,7 +24,7 @@ class ProjectFeature < ApplicationRecord
FEATURES = %i(issues merge_requests wiki snippets builds repository pages).freeze FEATURES = %i(issues merge_requests wiki snippets builds repository pages).freeze
PRIVATE_FEATURES_MIN_ACCESS_LEVEL = { merge_requests: Gitlab::Access::REPORTER }.freeze PRIVATE_FEATURES_MIN_ACCESS_LEVEL = { merge_requests: Gitlab::Access::REPORTER }.freeze
FEATURES_ALLOWED_BY_GUEST_ON_PRIVATE_PROJECT = %i(issues wiki).freeze PRIVATE_FEATURES_MIN_ACCESS_LEVEL_FOR_PRIVATE_PROJECT = { repository: Gitlab::Access::REPORTER }.freeze
STRING_OPTIONS = HashWithIndifferentAccess.new({ STRING_OPTIONS = HashWithIndifferentAccess.new({
'disabled' => DISABLED, 'disabled' => DISABLED,
'private' => PRIVATE, 'private' => PRIVATE,
...@@ -46,16 +46,19 @@ class ProjectFeature < ApplicationRecord ...@@ -46,16 +46,19 @@ class ProjectFeature < ApplicationRecord
"#{table}.#{attribute}" "#{table}.#{attribute}"
end end
def guest_allowed_on_private_project?(feature) def required_minimum_access_level(feature)
feature = ensure_feature!(feature) feature = ensure_feature!(feature)
FEATURES_ALLOWED_BY_GUEST_ON_PRIVATE_PROJECT.include?(feature) PRIVATE_FEATURES_MIN_ACCESS_LEVEL.fetch(feature, Gitlab::Access::GUEST)
end end
def required_minimum_access_level(feature) # Guest users can perform certain features on public and internal projects, but not private projects.
def required_minimum_access_level_for_private_project(feature)
feature = ensure_feature!(feature) feature = ensure_feature!(feature)
PRIVATE_FEATURES_MIN_ACCESS_LEVEL.fetch(feature, Gitlab::Access::GUEST) PRIVATE_FEATURES_MIN_ACCESS_LEVEL_FOR_PRIVATE_PROJECT.fetch(feature) do
required_minimum_access_level(feature)
end
end end
def access_level_from_str(level) def access_level_from_str(level)
... ...
......
...@@ -6,6 +6,16 @@ describe ProjectFeature do ...@@ -6,6 +6,16 @@ describe ProjectFeature do
let(:project) { create(:project) } let(:project) { create(:project) }
let(:user) { create(:user) } let(:user) { create(:user) }
describe 'PRIVATE_FEATURES_MIN_ACCESS_LEVEL_FOR_PRIVATE_PROJECT' do
it 'has higher level than that of PRIVATE_FEATURES_MIN_ACCESS_LEVEL' do
described_class::PRIVATE_FEATURES_MIN_ACCESS_LEVEL_FOR_PRIVATE_PROJECT.each do |feature, level|
if generic_level = described_class::PRIVATE_FEATURES_MIN_ACCESS_LEVEL[feature]
expect(level).to be >= generic_level
end
end
end
end
describe '.quoted_access_level_column' do describe '.quoted_access_level_column' do
it 'returns the table name and quoted column name for a feature' do it 'returns the table name and quoted column name for a feature' do
expected = '"project_features"."issues_access_level"' expected = '"project_features"."issues_access_level"'
...@@ -246,10 +256,24 @@ describe ProjectFeature do ...@@ -246,10 +256,24 @@ describe ProjectFeature do
expect(described_class.required_minimum_access_level('merge_requests')).to eq(Gitlab::Access::REPORTER) expect(described_class.required_minimum_access_level('merge_requests')).to eq(Gitlab::Access::REPORTER)
end end
it 'handles repository' do
expect(described_class.required_minimum_access_level(:repository)).to eq(Gitlab::Access::GUEST)
end
it 'raises error if feature is invalid' do it 'raises error if feature is invalid' do
expect do expect do
described_class.required_minimum_access_level(:foos) described_class.required_minimum_access_level(:foos)
end.to raise_error end.to raise_error
end end
end end
describe '.required_minimum_access_level_for_private_project' do
it 'returns higher permission for repository' do
expect(described_class.required_minimum_access_level_for_private_project(:repository)).to eq(Gitlab::Access::REPORTER)
end
it 'returns normal permission for issues' do
expect(described_class.required_minimum_access_level_for_private_project(:issues)).to eq(Gitlab::Access::GUEST)
end
end
end end