diff options
-rw-r--r-- | app/models/project.rb | 8 | ||||
-rw-r--r-- | app/models/project_feature.rb | 7 | ||||
-rw-r--r-- | spec/models/project_spec.rb | 74 |
3 files changed, 88 insertions, 1 deletions
diff --git a/app/models/project.rb b/app/models/project.rb index 74da042d5a5..624a4c5d38f 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -516,7 +516,13 @@ class Project < ApplicationRecord # This scope returns projects where user has access to both the project and the feature. def self.filter_by_feature_visibility(feature, user) - with_feature_available_for_user(feature, user).public_or_visible_to_user(user) + scope = with_feature_available_for_user(feature, user) + + if ProjectFeature.guest_allowed_on_private_project?(feature) + scope.public_or_visible_to_user(user) + else + scope.public_or_visible_to_user(user, Gitlab::Access::REPORTER) + end end scope :active, -> { joins(:issues, :notes, :merge_requests).order('issues.created_at, notes.created_at, merge_requests.created_at DESC') } diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb index 2013f620b5b..564e531c320 100644 --- a/app/models/project_feature.rb +++ b/app/models/project_feature.rb @@ -24,6 +24,7 @@ class ProjectFeature < ApplicationRecord FEATURES = %i(issues merge_requests wiki snippets builds repository pages).freeze PRIVATE_FEATURES_MIN_ACCESS_LEVEL = { merge_requests: Gitlab::Access::REPORTER }.freeze + FEATURES_ALLOWED_BY_GUEST_ON_PRIVATE_PROJECT = %i(issues wiki).freeze STRING_OPTIONS = HashWithIndifferentAccess.new({ 'disabled' => DISABLED, 'private' => PRIVATE, @@ -45,6 +46,12 @@ class ProjectFeature < ApplicationRecord "#{table}.#{attribute}" end + def guest_allowed_on_private_project?(feature) + feature = ensure_feature!(feature) + + FEATURES_ALLOWED_BY_GUEST_ON_PRIVATE_PROJECT.include?(feature) + end + def required_minimum_access_level(feature) feature = ensure_feature!(feature) diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 1bda3094e75..77d34d37bb6 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -3414,6 +3414,20 @@ describe Project do expect(projects).to eq([public_project]) end end + + context 'min_access_level' do + let!(:private_project) { create(:project, :private) } + + before do + private_project.add_guest(user) + end + + it 'excludes projects when user does not have required minimum access level' do + projects = described_class.all.public_or_visible_to_user(user, Gitlab::Access::REPORTER) + + expect(projects).to contain_exactly(public_project) + end + end end describe '.ids_with_issuables_available_for' do @@ -3565,6 +3579,66 @@ describe Project do end end end + + context 'issues' do + let(:feature) { Issue } + + where(:project_level, :feature_access_level, :membership, :expected_count) do + permission_table_for_guest_feature_access + end + + with_them do + it "respects visibility" do + update_feature_access_level(project, feature_access_level) + + expected_objects = expected_count == 1 ? [project] : [] + + expect( + described_class.filter_by_feature_visibility(feature, user) + ).to eq(expected_objects) + end + end + end + + context 'wiki' do + let(:feature) { :wiki } + + where(:project_level, :feature_access_level, :membership, :expected_count) do + permission_table_for_guest_feature_access + end + + with_them do + it "respects visibility" do + update_feature_access_level(project, feature_access_level) + + expected_objects = expected_count == 1 ? [project] : [] + + expect( + described_class.filter_by_feature_visibility(feature, user) + ).to eq(expected_objects) + end + end + end + + context 'code' do + let(:feature) { :repository } + + where(:project_level, :feature_access_level, :membership, :expected_count) do + permission_table_for_guest_feature_access_and_non_private_project_only + end + + with_them do + it "respects visibility" do + update_feature_access_level(project, feature_access_level) + + expected_objects = expected_count == 1 ? [project] : [] + + expect( + described_class.filter_by_feature_visibility(feature, user) + ).to eq(expected_objects) + end + end + end end describe '#pages_available?' do |