Fix bug validating EE project features

The current class `ProjectFeature` validates that none of the
project features have a value greater than `Featurable::ENABLED`,
except for the `pages` feature.

Nevertheless, we have an EE extension where we add more features to
the `ProjectFeature` class. The current validator use only the
constant `ProjectFeature::FEATURES` for the validation. This means
that the `ProjectFeature::EE_FEATURES` are not included in the
validation and we could set the `Featurable::PUBLIC` value.

In this commit we fix that by including all features set in the
`ProjectFeature` including both FOSS and EE ones.

Changelog: fixed
parent 78684055
...@@ -83,6 +83,10 @@ module Featurable ...@@ -83,6 +83,10 @@ module Featurable
end end
end end
included do
validate :allowed_access_levels
end
def access_level(feature) def access_level(feature)
public_send(self.class.access_level_attribute(feature)) # rubocop:disable GitlabSecurity/PublicSend public_send(self.class.access_level_attribute(feature)) # rubocop:disable GitlabSecurity/PublicSend
end end
...@@ -94,4 +98,21 @@ module Featurable ...@@ -94,4 +98,21 @@ module Featurable
def string_access_level(feature) def string_access_level(feature)
self.class.str_from_access_level(access_level(feature)) self.class.str_from_access_level(access_level(feature))
end end
private
def allowed_access_levels
validator = lambda do |field|
level = public_send(field) || ENABLED # rubocop:disable GitlabSecurity/PublicSend
not_allowed = level > ENABLED
self.errors.add(field, "cannot have public visibility level") if not_allowed
end
(self.class.available_features - feature_validation_exclusion).each {|f| validator.call("#{f}_access_level")}
end
# Features that we should exclude from the validation
def feature_validation_exclusion
[]
end
end end
...@@ -54,7 +54,6 @@ class ProjectFeature < ApplicationRecord ...@@ -54,7 +54,6 @@ class ProjectFeature < ApplicationRecord
validates :project, presence: true validates :project, presence: true
validate :repository_children_level validate :repository_children_level
validate :allowed_access_levels
default_value_for :builds_access_level, value: ENABLED, allows_nil: false default_value_for :builds_access_level, value: ENABLED, allows_nil: false
default_value_for :issues_access_level, value: ENABLED, allows_nil: false default_value_for :issues_access_level, value: ENABLED, allows_nil: false
...@@ -110,17 +109,6 @@ class ProjectFeature < ApplicationRecord ...@@ -110,17 +109,6 @@ class ProjectFeature < ApplicationRecord
%i(merge_requests_access_level builds_access_level).each(&validator) %i(merge_requests_access_level builds_access_level).each(&validator)
end end
# Validates access level for other than pages cannot be PUBLIC
def allowed_access_levels
validator = lambda do |field|
level = public_send(field) || ENABLED # rubocop:disable GitlabSecurity/PublicSend
not_allowed = level > ENABLED
self.errors.add(field, "cannot have public visibility level") if not_allowed
end
(FEATURES - %i(pages)).each {|f| validator.call("#{f}_access_level")}
end
def get_permission(user, feature) def get_permission(user, feature)
case access_level(feature) case access_level(feature)
when DISABLED when DISABLED
...@@ -142,6 +130,10 @@ class ProjectFeature < ApplicationRecord ...@@ -142,6 +130,10 @@ class ProjectFeature < ApplicationRecord
project.team.member?(user, ProjectFeature.required_minimum_access_level(feature)) project.team.member?(user, ProjectFeature.required_minimum_access_level(feature))
end end
def feature_validation_exclusion
%i(pages)
end
end end
ProjectFeature.prepend_mod_with('ProjectFeature') ProjectFeature.prepend_mod_with('ProjectFeature')
...@@ -54,4 +54,8 @@ RSpec.describe ProjectFeature do ...@@ -54,4 +54,8 @@ RSpec.describe ProjectFeature do
end end
end end
end end
it_behaves_like 'access level validation', ProjectFeature::EE_FEATURES do
let(:container_features) { project.project_feature }
end
end end
...@@ -30,8 +30,11 @@ RSpec.describe Featurable do ...@@ -30,8 +30,11 @@ RSpec.describe Featurable do
describe '.set_available_features' do describe '.set_available_features' do
let!(:klass) do let!(:klass) do
Class.new do Class.new(ApplicationRecord) do
include Featurable include Featurable
self.table_name = 'project_features'
set_available_features %i(feature1 feature2) set_available_features %i(feature1 feature2)
def feature1_access_level def feature1_access_level
......
...@@ -41,18 +41,15 @@ RSpec.describe ProjectFeature do ...@@ -41,18 +41,15 @@ RSpec.describe ProjectFeature do
end end
end end
context 'public features' do it_behaves_like 'access level validation', ProjectFeature::FEATURES - %i(pages) do
features = ProjectFeature::FEATURES - %i(pages) let(:container_features) { project.project_feature }
end
features.each do |feature| it 'allows public access level for :pages feature' do
it "does not allow public access level for #{feature}" do project_feature = project.project_feature
project_feature = project.project_feature project_feature.pages_access_level = ProjectFeature::PUBLIC
field = "#{feature}_access_level".to_sym
project_feature.update_attribute(field, ProjectFeature::PUBLIC)
expect(project_feature.valid?).to be_falsy, "#{field} failed" expect(project_feature.valid?).to be_truthy
end
end
end end
describe 'default pages access level' do describe 'default pages access level' do
......
# frozen_string_literal: true
RSpec.shared_examples 'access level validation' do |features|
features.each do |feature|
it "does not allow public access level for #{feature}" do
field = "#{feature}_access_level".to_sym
container_features.update_attribute(field, ProjectFeature::PUBLIC)
expect(container_features.valid?).to be_falsy, "#{field} failed"
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