2019-03-30 03:23:56 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2016-08-01 18:31:21 -04:00
|
|
|
require 'spec_helper'
|
|
|
|
|
2020-06-24 14:09:03 -04:00
|
|
|
RSpec.describe ProjectFeature do
|
2020-01-14 13:08:31 -05:00
|
|
|
using RSpec::Parameterized::TableSyntax
|
|
|
|
|
2022-04-07 08:10:21 -04:00
|
|
|
let_it_be_with_reload(:project) { create(:project) }
|
|
|
|
let_it_be(:user) { create(:user) }
|
2016-08-01 18:31:21 -04:00
|
|
|
|
2021-08-16 08:09:17 -04:00
|
|
|
it { is_expected.to belong_to(:project) }
|
|
|
|
|
2019-11-13 21:50:19 -05:00
|
|
|
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
|
|
|
|
|
2016-09-16 15:15:39 -04:00
|
|
|
context 'repository related features' do
|
|
|
|
before do
|
2021-05-03 14:10:17 -04:00
|
|
|
project.project_feature.update!(
|
2016-09-16 15:15:39 -04:00
|
|
|
merge_requests_access_level: ProjectFeature::DISABLED,
|
|
|
|
builds_access_level: ProjectFeature::DISABLED,
|
|
|
|
repository_access_level: ProjectFeature::PRIVATE
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "does not allow repository related features have higher level" do
|
|
|
|
features = %w(builds merge_requests)
|
|
|
|
project_feature = project.project_feature
|
|
|
|
|
|
|
|
features.each do |feature|
|
|
|
|
field = "#{feature}_access_level".to_sym
|
|
|
|
project_feature.update_attribute(field, ProjectFeature::ENABLED)
|
2020-05-15 11:08:04 -04:00
|
|
|
expect(project_feature.valid?).to be_falsy, "#{field} failed"
|
2016-09-16 15:15:39 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-05-30 11:08:03 -04:00
|
|
|
it_behaves_like 'access level validation', ProjectFeature::FEATURES - %i(pages package_registry) do
|
2021-08-24 05:10:45 -04:00
|
|
|
let(:container_features) { project.project_feature }
|
|
|
|
end
|
2018-10-05 09:41:11 -04:00
|
|
|
|
2021-08-24 05:10:45 -04:00
|
|
|
it 'allows public access level for :pages feature' do
|
|
|
|
project_feature = project.project_feature
|
|
|
|
project_feature.pages_access_level = ProjectFeature::PUBLIC
|
2020-01-14 13:08:31 -05:00
|
|
|
|
2021-08-24 05:10:45 -04:00
|
|
|
expect(project_feature.valid?).to be_truthy
|
2018-10-05 09:41:11 -04:00
|
|
|
end
|
|
|
|
|
2019-07-17 08:56:58 -04:00
|
|
|
describe 'default pages access level' do
|
2020-01-14 13:08:31 -05:00
|
|
|
subject { project_feature.pages_access_level }
|
2019-07-17 08:56:58 -04:00
|
|
|
|
2020-01-14 13:08:31 -05:00
|
|
|
let(:project_feature) do
|
2019-07-17 08:56:58 -04:00
|
|
|
# project factory overrides all values in project_feature after creation
|
|
|
|
project.project_feature.destroy!
|
|
|
|
project.build_project_feature.save!
|
2020-01-14 13:08:31 -05:00
|
|
|
project.project_feature
|
2019-07-17 08:56:58 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'when new project is private' do
|
|
|
|
let(:project) { create(:project, :private) }
|
|
|
|
|
|
|
|
it { is_expected.to eq(ProjectFeature::PRIVATE) }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when new project is internal' do
|
|
|
|
let(:project) { create(:project, :internal) }
|
|
|
|
|
|
|
|
it { is_expected.to eq(ProjectFeature::PRIVATE) }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when new project is public' do
|
|
|
|
let(:project) { create(:project, :public) }
|
|
|
|
|
|
|
|
it { is_expected.to eq(ProjectFeature::ENABLED) }
|
2020-01-14 13:08:31 -05:00
|
|
|
|
|
|
|
context 'when access control is forced on the admin level' do
|
|
|
|
before do
|
|
|
|
allow(::Gitlab::Pages).to receive(:access_control_is_forced?).and_return(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to eq(ProjectFeature::PRIVATE) }
|
|
|
|
end
|
2019-07-17 08:56:58 -04:00
|
|
|
end
|
|
|
|
end
|
2019-09-10 22:25:21 -04:00
|
|
|
|
|
|
|
describe '#public_pages?' do
|
2020-05-15 11:08:04 -04:00
|
|
|
it 'returns true if Pages access control is not enabled' do
|
2019-09-10 22:25:21 -04:00
|
|
|
stub_config(pages: { access_control: false })
|
|
|
|
|
2020-01-14 13:08:31 -05:00
|
|
|
project_feature = described_class.new(pages_access_level: described_class::PRIVATE)
|
2019-09-10 22:25:21 -04:00
|
|
|
|
|
|
|
expect(project_feature.public_pages?).to eq(true)
|
|
|
|
end
|
|
|
|
|
2020-01-14 13:08:31 -05:00
|
|
|
context 'when Pages access control is enabled' do
|
2019-09-10 22:25:21 -04:00
|
|
|
before do
|
|
|
|
stub_config(pages: { access_control: true })
|
|
|
|
end
|
|
|
|
|
2020-01-14 13:08:31 -05:00
|
|
|
where(:project_visibility, :pages_access_level, :result) do
|
|
|
|
:private | ProjectFeature::PUBLIC | true
|
|
|
|
:internal | ProjectFeature::PUBLIC | true
|
|
|
|
:internal | ProjectFeature::ENABLED | false
|
|
|
|
:public | ProjectFeature::ENABLED | true
|
|
|
|
:private | ProjectFeature::PRIVATE | false
|
|
|
|
:public | ProjectFeature::PRIVATE | false
|
2019-09-10 22:25:21 -04:00
|
|
|
end
|
|
|
|
|
2020-01-14 13:08:31 -05:00
|
|
|
with_them do
|
|
|
|
let(:project_feature) do
|
|
|
|
project = build(:project, project_visibility)
|
|
|
|
project_feature = project.project_feature
|
|
|
|
project_feature.update!(pages_access_level: pages_access_level)
|
|
|
|
project_feature
|
|
|
|
end
|
2019-09-10 22:25:21 -04:00
|
|
|
|
2020-01-14 13:08:31 -05:00
|
|
|
it 'properly handles project and Pages visibility settings' do
|
|
|
|
expect(project_feature.public_pages?).to eq(result)
|
|
|
|
end
|
2019-09-10 22:25:21 -04:00
|
|
|
|
2020-01-14 13:08:31 -05:00
|
|
|
it 'returns false if access_control is forced on the admin level' do
|
|
|
|
stub_application_setting(force_pages_access_control: true)
|
2019-09-10 22:25:21 -04:00
|
|
|
|
2020-01-14 13:08:31 -05:00
|
|
|
expect(project_feature.public_pages?).to eq(false)
|
|
|
|
end
|
2019-09-10 22:25:21 -04:00
|
|
|
end
|
|
|
|
end
|
2020-01-14 13:08:31 -05:00
|
|
|
end
|
2019-09-10 22:25:21 -04:00
|
|
|
|
2020-01-14 13:08:31 -05:00
|
|
|
describe '#private_pages?' do
|
|
|
|
subject(:project_feature) { described_class.new }
|
2019-09-10 22:25:21 -04:00
|
|
|
|
2020-01-14 13:08:31 -05:00
|
|
|
it 'returns false if public_pages? is true' do
|
|
|
|
expect(project_feature).to receive(:public_pages?).and_return(true)
|
2019-09-10 22:25:21 -04:00
|
|
|
|
2020-01-14 13:08:31 -05:00
|
|
|
expect(project_feature.private_pages?).to eq(false)
|
|
|
|
end
|
2019-09-10 22:25:21 -04:00
|
|
|
|
2020-01-14 13:08:31 -05:00
|
|
|
it 'returns true if public_pages? is false' do
|
|
|
|
expect(project_feature).to receive(:public_pages?).and_return(false)
|
2019-09-10 22:25:21 -04:00
|
|
|
|
2020-01-14 13:08:31 -05:00
|
|
|
expect(project_feature.private_pages?).to eq(true)
|
2019-09-10 22:25:21 -04:00
|
|
|
end
|
|
|
|
end
|
2019-10-02 05:05:53 -04:00
|
|
|
|
|
|
|
describe '.required_minimum_access_level' do
|
|
|
|
it 'handles reporter level' do
|
|
|
|
expect(described_class.required_minimum_access_level(:merge_requests)).to eq(Gitlab::Access::REPORTER)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'handles guest level' do
|
|
|
|
expect(described_class.required_minimum_access_level(:issues)).to eq(Gitlab::Access::GUEST)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'accepts ActiveModel' do
|
|
|
|
expect(described_class.required_minimum_access_level(MergeRequest)).to eq(Gitlab::Access::REPORTER)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'accepts string' do
|
|
|
|
expect(described_class.required_minimum_access_level('merge_requests')).to eq(Gitlab::Access::REPORTER)
|
|
|
|
end
|
|
|
|
|
2019-11-13 21:50:19 -05:00
|
|
|
it 'handles repository' do
|
|
|
|
expect(described_class.required_minimum_access_level(:repository)).to eq(Gitlab::Access::GUEST)
|
|
|
|
end
|
|
|
|
|
2022-05-30 11:08:03 -04:00
|
|
|
it 'handles package registry' do
|
|
|
|
expect(described_class.required_minimum_access_level(:package_registry)).to eq(Gitlab::Access::REPORTER)
|
|
|
|
end
|
|
|
|
|
2019-10-02 05:05:53 -04:00
|
|
|
it 'raises error if feature is invalid' do
|
|
|
|
expect do
|
|
|
|
described_class.required_minimum_access_level(:foos)
|
2020-05-15 11:08:04 -04:00
|
|
|
end.to raise_error(ArgumentError)
|
2019-10-02 05:05:53 -04:00
|
|
|
end
|
|
|
|
end
|
2019-11-13 21:50:19 -05:00
|
|
|
|
|
|
|
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
|
2021-03-12 16:09:12 -05:00
|
|
|
|
|
|
|
describe 'container_registry_access_level' do
|
2021-07-26 08:10:08 -04:00
|
|
|
context 'with default value' do
|
|
|
|
let(:project) { Project.new }
|
2021-03-12 16:09:12 -05:00
|
|
|
|
2021-07-26 08:10:08 -04:00
|
|
|
context 'when the default is false' do
|
|
|
|
it 'creates project_feature with `disabled` container_registry_access_level' do
|
|
|
|
stub_config_setting(default_projects_features: { container_registry: false })
|
|
|
|
|
|
|
|
expect(project.project_feature.container_registry_access_level).to eq(described_class::DISABLED)
|
|
|
|
end
|
2021-03-12 16:09:12 -05:00
|
|
|
end
|
|
|
|
|
2021-07-26 08:10:08 -04:00
|
|
|
context 'when the default is true' do
|
|
|
|
before do
|
|
|
|
stub_config_setting(default_projects_features: { container_registry: true })
|
|
|
|
end
|
2021-03-12 16:09:12 -05:00
|
|
|
|
2021-07-26 08:10:08 -04:00
|
|
|
it 'creates project_feature with `enabled` container_registry_access_level' do
|
|
|
|
expect(project.project_feature.container_registry_access_level).to eq(described_class::ENABLED)
|
|
|
|
end
|
2021-03-12 16:09:12 -05:00
|
|
|
end
|
|
|
|
|
2021-07-26 08:10:08 -04:00
|
|
|
context 'when the default is nil' do
|
|
|
|
it 'creates project_feature with `disabled` container_registry_access_level' do
|
|
|
|
stub_config_setting(default_projects_features: { container_registry: nil })
|
2021-03-12 16:09:12 -05:00
|
|
|
|
2021-07-26 08:10:08 -04:00
|
|
|
expect(project.project_feature.container_registry_access_level).to eq(described_class::DISABLED)
|
|
|
|
end
|
2021-03-12 16:09:12 -05:00
|
|
|
end
|
|
|
|
end
|
2021-08-04 17:09:04 -04:00
|
|
|
|
|
|
|
context 'test build factory' do
|
|
|
|
let(:project) { build(:project, container_registry_access_level: level) }
|
|
|
|
|
|
|
|
subject { project.container_registry_access_level }
|
|
|
|
|
|
|
|
context 'private' do
|
|
|
|
let(:level) { ProjectFeature::PRIVATE }
|
|
|
|
|
|
|
|
it { is_expected.to eq(level) }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'enabled' do
|
|
|
|
let(:level) { ProjectFeature::ENABLED }
|
|
|
|
|
|
|
|
it { is_expected.to eq(level) }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'disabled' do
|
|
|
|
let(:level) { ProjectFeature::DISABLED }
|
|
|
|
|
|
|
|
it { is_expected.to eq(level) }
|
|
|
|
end
|
|
|
|
end
|
2021-03-12 16:09:12 -05:00
|
|
|
end
|
2022-04-07 08:10:21 -04:00
|
|
|
|
2022-05-30 11:08:03 -04:00
|
|
|
describe 'package_registry_access_level' do
|
|
|
|
context 'with default value' do
|
|
|
|
where(:config_packages_enabled, :expected_result) do
|
|
|
|
false | ProjectFeature::DISABLED
|
|
|
|
true | ProjectFeature::ENABLED
|
|
|
|
nil | ProjectFeature::DISABLED
|
|
|
|
end
|
|
|
|
|
|
|
|
with_them do
|
|
|
|
it 'creates project_feature with correct package_registry_access_level' do
|
|
|
|
stub_packages_setting(enabled: config_packages_enabled)
|
|
|
|
project = Project.new
|
|
|
|
|
|
|
|
expect(project.project_feature.package_registry_access_level).to eq(expected_result)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'sync packages_enabled' do
|
|
|
|
# rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
|
|
|
|
where(:initial_value, :new_value, :expected_result) do
|
|
|
|
ProjectFeature::DISABLED | ProjectFeature::DISABLED | false
|
|
|
|
ProjectFeature::DISABLED | ProjectFeature::ENABLED | true
|
|
|
|
ProjectFeature::DISABLED | ProjectFeature::PUBLIC | true
|
|
|
|
ProjectFeature::ENABLED | ProjectFeature::DISABLED | false
|
|
|
|
ProjectFeature::ENABLED | ProjectFeature::ENABLED | true
|
|
|
|
ProjectFeature::ENABLED | ProjectFeature::PUBLIC | true
|
|
|
|
ProjectFeature::PUBLIC | ProjectFeature::DISABLED | false
|
|
|
|
ProjectFeature::PUBLIC | ProjectFeature::ENABLED | true
|
|
|
|
ProjectFeature::PUBLIC | ProjectFeature::PUBLIC | true
|
|
|
|
end
|
|
|
|
# rubocop:enable Lint/BinaryOperatorWithIdenticalOperands
|
|
|
|
|
|
|
|
with_them do
|
|
|
|
it 'set correct value' do
|
|
|
|
project = create(:project, package_registry_access_level: initial_value)
|
|
|
|
|
|
|
|
project.project_feature.update!(package_registry_access_level: new_value)
|
|
|
|
|
|
|
|
expect(project.packages_enabled).to eq(expected_result)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2022-04-07 08:10:21 -04:00
|
|
|
# rubocop:disable Gitlab/FeatureAvailableUsage
|
|
|
|
describe '#feature_available?' do
|
|
|
|
let(:features) { ProjectFeature::FEATURES }
|
|
|
|
|
|
|
|
context 'when features are disabled' do
|
|
|
|
it 'returns false' do
|
|
|
|
update_all_project_features(project, features, ProjectFeature::DISABLED)
|
|
|
|
|
|
|
|
features.each do |feature|
|
|
|
|
expect(project.feature_available?(feature.to_sym, user)).to eq(false), "#{feature} failed"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when features are enabled only for team members' do
|
|
|
|
it 'returns false when user is not a team member' do
|
|
|
|
update_all_project_features(project, features, ProjectFeature::PRIVATE)
|
|
|
|
|
|
|
|
features.each do |feature|
|
|
|
|
expect(project.feature_available?(feature.to_sym, user)).to eq(false), "#{feature} failed"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns true when user is a team member' do
|
|
|
|
project.add_developer(user)
|
|
|
|
|
|
|
|
update_all_project_features(project, features, ProjectFeature::PRIVATE)
|
|
|
|
|
|
|
|
features.each do |feature|
|
|
|
|
expect(project.feature_available?(feature.to_sym, user)).to eq(true)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns true when user is a member of project group' do
|
|
|
|
group = create(:group)
|
|
|
|
project = create(:project, namespace: group)
|
|
|
|
group.add_developer(user)
|
|
|
|
|
|
|
|
update_all_project_features(project, features, ProjectFeature::PRIVATE)
|
|
|
|
|
|
|
|
features.each do |feature|
|
|
|
|
expect(project.feature_available?(feature.to_sym, user)).to eq(true)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when admin mode is enabled', :enable_admin_mode do
|
|
|
|
it 'returns true if user is an admin' do
|
|
|
|
user.update_attribute(:admin, true)
|
|
|
|
|
|
|
|
update_all_project_features(project, features, ProjectFeature::PRIVATE)
|
|
|
|
|
|
|
|
features.each do |feature|
|
|
|
|
expect(project.feature_available?(feature.to_sym, user)).to eq(true)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when admin mode is disabled' do
|
|
|
|
it 'returns false when user is an admin' do
|
|
|
|
user.update_attribute(:admin, true)
|
|
|
|
|
|
|
|
update_all_project_features(project, features, ProjectFeature::PRIVATE)
|
|
|
|
|
|
|
|
features.each do |feature|
|
|
|
|
expect(project.feature_available?(feature.to_sym, user)).to eq(false), "#{feature} failed"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when feature is enabled for everyone' do
|
|
|
|
it 'returns true' do
|
|
|
|
expect(project.feature_available?(:issues, user)).to eq(true)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when feature has any other value' do
|
|
|
|
it 'returns true' do
|
|
|
|
project.project_feature.update_attribute(:issues_access_level, 200)
|
|
|
|
|
|
|
|
expect(project.feature_available?(:issues)).to eq(true)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def update_all_project_features(project, features, value)
|
|
|
|
project_feature_attributes = features.to_h { |f| ["#{f}_access_level", value] }
|
|
|
|
project.project_feature.update!(project_feature_attributes)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
# rubocop:enable Gitlab/FeatureAvailableUsage
|
2016-08-01 18:31:21 -04:00
|
|
|
end
|