Skip to content
Snippets Groups Projects
Commit a03d4fa2 authored by Alishan Ladhani's avatar Alishan Ladhani :bangbang:
Browse files

Split environments visibility setting from operations

parent 7adcaa9b
No related branches found
No related tags found
1 merge request!93118Split environments visibility setting from operations
Showing with 133 additions and 25 deletions
......@@ -639,7 +639,8 @@ def project_permissions_settings(project)
warnAboutPotentiallyUnwantedCharacters: project.warn_about_potentially_unwanted_characters?,
enforceAuthChecksOnUploads: project.enforce_auth_checks_on_uploads?,
securityAndComplianceAccessLevel: project.security_and_compliance_access_level,
containerRegistryAccessLevel: feature.container_registry_access_level
containerRegistryAccessLevel: feature.container_registry_access_level,
environmentsAccessLevel: feature.environments_access_level
}
end
......
......@@ -94,6 +94,10 @@ def container_registry_access_level=(value)
write_feature_attribute_string(:container_registry_access_level, value)
end
def environments_access_level=(value)
write_feature_attribute_string(:environments_access_level, value)
end
# TODO: Remove this method after we drop support for project create/edit APIs to set the
# container_registry_enabled attribute. They can instead set the container_registry_access_level
# attribute.
......
......@@ -446,7 +446,7 @@ def self.integration_association_name(name)
:repository_access_level, :package_registry_access_level, :pages_access_level,
:metrics_dashboard_access_level, :analytics_access_level,
:operations_access_level, :security_and_compliance_access_level,
:container_registry_access_level,
:container_registry_access_level, :environments_access_level,
to: :project_feature, allow_nil: true
delegate :show_default_award_emojis, :show_default_award_emojis=,
......
......@@ -21,6 +21,7 @@ class ProjectFeature < ApplicationRecord
security_and_compliance
container_registry
package_registry
environments
].freeze
EXPORTABLE_FEATURES = (FEATURES - [:security_and_compliance, :pages]).freeze
......
......@@ -209,6 +209,7 @@ class ProjectPolicy < BasePolicy
analytics
operations
security_and_compliance
environments
]
features.each do |f|
......@@ -366,7 +367,11 @@ class ProjectPolicy < BasePolicy
prevent(:metrics_dashboard)
end
rule { operations_disabled }.policy do
condition(:split_operations_visibility_permissions) do
::Feature.enabled?(:split_operations_visibility_permissions, @subject)
end
rule { ~split_operations_visibility_permissions & operations_disabled }.policy do
prevent(*create_read_update_admin_destroy(:feature_flag))
prevent(*create_read_update_admin_destroy(:environment))
prevent(*create_read_update_admin_destroy(:sentry_issue))
......@@ -379,6 +384,11 @@ class ProjectPolicy < BasePolicy
prevent(:read_prometheus)
end
rule { split_operations_visibility_permissions & environments_disabled }.policy do
prevent(*create_read_update_admin_destroy(:environment))
prevent(*create_read_update_admin_destroy(:deployment))
end
rule { can?(:metrics_dashboard) }.policy do
enable :read_prometheus
enable :read_deployment
......
......@@ -284,6 +284,7 @@ included_attributes:
- :security_and_compliance_access_level
- :container_registry_access_level
- :package_registry_access_level
- :environments_access_level
prometheus_metrics:
- :created_at
- :updated_at
......@@ -686,6 +687,7 @@ included_attributes:
- :security_and_compliance_access_level
- :container_registry_access_level
- :package_registry_access_level
- :environments_access_level
- :allow_merge_on_skipped_pipeline
- :auto_devops_deploy_strategy
- :auto_devops_enabled
......
......@@ -23,6 +23,7 @@ class FeatureAvailableUsage < RuboCop::Cop::Cop
operations
security_and_compliance
container_registry
environments
].freeze
EE_FEATURES = %i[requirements].freeze
ALL_FEATURES = (FEATURES + EE_FEATURES).freeze
......
......@@ -37,6 +37,7 @@
operations_access_level { ProjectFeature::ENABLED }
container_registry_access_level { ProjectFeature::ENABLED }
security_and_compliance_access_level { ProjectFeature::PRIVATE }
environments_access_level { ProjectFeature::ENABLED }
# we can't assign the delegated `#ci_cd_settings` attributes directly, as the
# `#ci_cd_settings` relation needs to be created first
......
......@@ -966,7 +966,8 @@ def license_name
operationsAccessLevel: project.project_feature.operations_access_level,
showDefaultAwardEmojis: project.show_default_award_emojis?,
securityAndComplianceAccessLevel: project.security_and_compliance_access_level,
containerRegistryAccessLevel: project.project_feature.container_registry_access_level
containerRegistryAccessLevel: project.project_feature.container_registry_access_level,
environmentsAccessLevel: project.project_feature.environments_access_level
)
end
......
......@@ -584,6 +584,7 @@ ProjectFeature:
- security_and_compliance_access_level
- container_registry_access_level
- package_registry_access_level
- environments_access_level
- created_at
- updated_at
ProtectedBranch::MergeAccessLevel:
......
......@@ -5,7 +5,7 @@
RSpec.describe ProjectFeaturesCompatibility do
let(:project) { create(:project) }
let(:features_enabled) { %w(issues wiki builds merge_requests snippets security_and_compliance) }
let(:features) { features_enabled + %w(repository pages operations container_registry package_registry) }
let(:features) { features_enabled + %w(repository pages operations container_registry package_registry environments) }
# We had issues_enabled, snippets_enabled, builds_enabled, merge_requests_enabled and issues_enabled fields on projects table
# All those fields got moved to a new table called project_feature and are now integers instead of booleans
......
......@@ -831,6 +831,7 @@
it { is_expected.to delegate_method(:last_pipeline).to(:commit).allow_nil }
it { is_expected.to delegate_method(:container_registry_enabled?).to(:project_feature) }
it { is_expected.to delegate_method(:container_registry_access_level).to(:project_feature) }
it { is_expected.to delegate_method(:environments_access_level).to(:project_feature) }
describe 'read project settings' do
%i(
......
......@@ -1930,6 +1930,10 @@ def set_access_level(access_level)
describe 'operations feature' do
using RSpec::Parameterized::TableSyntax
before do
stub_feature_flags(split_operations_visibility_permissions: false)
end
let(:guest_operations_permissions) { [:read_environment, :read_deployment] }
let(:developer_operations_permissions) do
......@@ -2002,38 +2006,95 @@ def set_access_level(access_level)
end
end
def project_subject(project_type)
case project_type
when :public
public_project
when :internal
internal_project
def permissions_abilities(role)
case role
when :maintainer
maintainer_operations_permissions
when :developer
developer_operations_permissions
else
private_project
guest_operations_permissions
end
end
end
end
def user_subject(role)
case role
when :maintainer
maintainer
when :developer
developer
when :guest
guest
when :anonymous
anonymous
describe 'environments feature' do
using RSpec::Parameterized::TableSyntax
let(:guest_environments_permissions) { [:read_environment, :read_deployment] }
let(:developer_environments_permissions) do
guest_environments_permissions + [
:create_environment, :create_deployment, :update_environment, :update_deployment, :destroy_environment
]
end
let(:maintainer_environments_permissions) do
developer_environments_permissions + [:admin_environment, :admin_deployment]
end
where(:project_visibility, :access_level, :role, :allowed) do
:public | ProjectFeature::ENABLED | :maintainer | true
:public | ProjectFeature::ENABLED | :developer | true
:public | ProjectFeature::ENABLED | :guest | true
:public | ProjectFeature::ENABLED | :anonymous | true
:public | ProjectFeature::PRIVATE | :maintainer | true
:public | ProjectFeature::PRIVATE | :developer | true
:public | ProjectFeature::PRIVATE | :guest | true
:public | ProjectFeature::PRIVATE | :anonymous | false
:public | ProjectFeature::DISABLED | :maintainer | false
:public | ProjectFeature::DISABLED | :developer | false
:public | ProjectFeature::DISABLED | :guest | false
:public | ProjectFeature::DISABLED | :anonymous | false
:internal | ProjectFeature::ENABLED | :maintainer | true
:internal | ProjectFeature::ENABLED | :developer | true
:internal | ProjectFeature::ENABLED | :guest | true
:internal | ProjectFeature::ENABLED | :anonymous | false
:internal | ProjectFeature::PRIVATE | :maintainer | true
:internal | ProjectFeature::PRIVATE | :developer | true
:internal | ProjectFeature::PRIVATE | :guest | true
:internal | ProjectFeature::PRIVATE | :anonymous | false
:internal | ProjectFeature::DISABLED | :maintainer | false
:internal | ProjectFeature::DISABLED | :developer | false
:internal | ProjectFeature::DISABLED | :guest | false
:internal | ProjectFeature::DISABLED | :anonymous | false
:private | ProjectFeature::ENABLED | :maintainer | true
:private | ProjectFeature::ENABLED | :developer | true
:private | ProjectFeature::ENABLED | :guest | false
:private | ProjectFeature::ENABLED | :anonymous | false
:private | ProjectFeature::PRIVATE | :maintainer | true
:private | ProjectFeature::PRIVATE | :developer | true
:private | ProjectFeature::PRIVATE | :guest | false
:private | ProjectFeature::PRIVATE | :anonymous | false
:private | ProjectFeature::DISABLED | :maintainer | false
:private | ProjectFeature::DISABLED | :developer | false
:private | ProjectFeature::DISABLED | :guest | false
:private | ProjectFeature::DISABLED | :anonymous | false
end
with_them do
let(:current_user) { user_subject(role) }
let(:project) { project_subject(project_visibility) }
it 'allows/disallows the abilities based on the environments feature access level' do
project.project_feature.update!(environments_access_level: access_level)
if allowed
expect_allowed(*permissions_abilities(role))
else
expect_disallowed(*permissions_abilities(role))
end
end
def permissions_abilities(role)
case role
when :maintainer
maintainer_operations_permissions
maintainer_environments_permissions
when :developer
developer_operations_permissions
developer_environments_permissions
else
guest_operations_permissions
guest_environments_permissions
end
end
end
......@@ -2481,4 +2542,28 @@ def permissions_abilities(role)
end
end
end
def project_subject(project_type)
case project_type
when :public
public_project
when :internal
internal_project
else
private_project
end
end
def user_subject(role)
case role
when :maintainer
maintainer
when :developer
developer
when :guest
guest
when :anonymous
anonymous
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment