Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
f3a8759a
Commit
f3a8759a
authored
Mar 16, 2022
by
Fabio Pitino
Committed by
Sean McGivern
Mar 16, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Extract GitlabSubscriptions::Features class
parent
95b17b01
Changes
21
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
568 additions
and
395 deletions
+568
-395
app/models/integrations/zentao.rb
app/models/integrations/zentao.rb
+0
-1
doc/development/licensed_feature_availability.md
doc/development/licensed_feature_availability.md
+4
-5
ee/app/controllers/subscriptions_controller.rb
ee/app/controllers/subscriptions_controller.rb
+1
-1
ee/app/helpers/admin/emails_helper.rb
ee/app/helpers/admin/emails_helper.rb
+1
-1
ee/app/helpers/admin/ip_restriction_helper.rb
ee/app/helpers/admin/ip_restriction_helper.rb
+1
-1
ee/app/helpers/admin/repo_size_limit_helper.rb
ee/app/helpers/admin/repo_size_limit_helper.rb
+1
-1
ee/app/models/ee/namespace.rb
ee/app/models/ee/namespace.rb
+2
-10
ee/app/models/ee/project.rb
ee/app/models/ee/project.rb
+1
-1
ee/app/models/gitlab_subscriptions/features.rb
ee/app/models/gitlab_subscriptions/features.rb
+301
-0
ee/app/models/license.rb
ee/app/models/license.rb
+5
-272
ee/app/services/gitlab_subscriptions/create_service.rb
ee/app/services/gitlab_subscriptions/create_service.rb
+1
-1
ee/lib/ee/api/features.rb
ee/lib/ee/api/features.rb
+1
-1
ee/lib/gitlab/ip_restriction/enforcer.rb
ee/lib/gitlab/ip_restriction/enforcer.rb
+1
-1
ee/spec/controllers/subscriptions_controller_spec.rb
ee/spec/controllers/subscriptions_controller_spec.rb
+1
-1
ee/spec/lib/ee/gitlab/gon_helper_spec.rb
ee/spec/lib/ee/gitlab/gon_helper_spec.rb
+1
-1
ee/spec/models/gitlab_subscriptions/features_spec.rb
ee/spec/models/gitlab_subscriptions/features_spec.rb
+239
-0
ee/spec/models/license_spec.rb
ee/spec/models/license_spec.rb
+1
-90
ee/spec/models/project_spec.rb
ee/spec/models/project_spec.rb
+3
-3
ee/spec/requests/api/features_spec.rb
ee/spec/requests/api/features_spec.rb
+1
-1
ee/spec/services/gitlab_subscriptions/create_service_spec.rb
ee/spec/services/gitlab_subscriptions/create_service_spec.rb
+1
-1
ee/spec/support/helpers/ee/license_helpers.rb
ee/spec/support/helpers/ee/license_helpers.rb
+1
-2
No files found.
app/models/integrations/zentao.rb
View file @
f3a8759a
...
...
@@ -11,7 +11,6 @@ module Integrations
validates
:api_token
,
presence:
true
,
if: :activated?
validates
:zentao_product_xid
,
presence:
true
,
if: :activated?
# License Level: EEP_FEATURES
def
self
.
issues_license_available?
(
project
)
project
&
.
licensed_feature_available?
(
:zentao_issues_integration
)
end
...
...
doc/development/licensed_feature_availability.md
View file @
f3a8759a
...
...
@@ -17,9 +17,8 @@ feature such as [Related issues](../user/project/issues/related_issues.md) or
[
Service Desk
](
../user/project/service_desk.md
)
,
it should be restricted on namespace scope.
1.
Add the feature symbol on
`EES_FEATURES`
,
`EEP_FEATURES`
, or
`EEU_FEATURES`
constants in
`ee/app/models/license.rb`
. Note that the prefix
`EES`
signifies Starter,
`EEP`
signifies
Premium, and
`EEU`
signifies Ultimate.
1.
Add the feature symbol on
`STARTER_FEATURES`
,
`PREMIUM_FEATURES`
, or
`ULTIMATE_FEATURES`
constants in
`ee/app/models/gitlab_subscriptions/features.rb`
.
1.
Check using:
```
ruby
...
...
@@ -33,8 +32,8 @@ However, for features such as [Geo](../administration/geo/index.md) and
to only a subset of projects or namespaces, the check is made directly in
the instance license.
1.
Add the feature symbol
on
`EES_FEATURES`
,
`EEP_FEATURES`
or
`EEU
_FEATURES`
constants in
`ee/app/models/
license
.rb`
.
1.
Add the feature symbol
to
`STARTER_FEATURES`
,
`PREMIUM_FEATURES`
or
`ULTIMATE
_FEATURES`
constants in
`ee/app/models/
gitlab_subscriptions/features
.rb`
.
1.
Add the same feature symbol to
`GLOBAL_FEATURES`
.
1.
Check using:
...
...
ee/app/controllers/subscriptions_controller.rb
View file @
f3a8759a
...
...
@@ -95,7 +95,7 @@ class SubscriptionsController < ApplicationController
return
render
json:
group
.
errors
.
to_json
end
response
=
Subscriptions
::
CreateService
.
new
(
response
=
Gitlab
Subscriptions
::
CreateService
.
new
(
current_user
,
group:
group
,
customer_params:
customer_params
,
...
...
ee/app/helpers/admin/emails_helper.rb
View file @
f3a8759a
...
...
@@ -5,7 +5,7 @@ module Admin
include
Gitlab
::
Utils
::
StrongMemoize
def
send_emails_from_admin_area_feature_available?
License
.
feature_available?
(
:send_emails_from_admin_area
)
||
License
.
features_with_usage_ping
.
includ
e?
(
:send_emails_from_admin_area
)
License
.
feature_available?
(
:send_emails_from_admin_area
)
||
GitlabSubscriptions
::
Features
.
usage_ping_featur
e?
(
:send_emails_from_admin_area
)
end
def
admin_emails_are_currently_rate_limited?
...
...
ee/app/helpers/admin/ip_restriction_helper.rb
View file @
f3a8759a
...
...
@@ -3,7 +3,7 @@
module
Admin
module
IpRestrictionHelper
def
ip_restriction_feature_available?
(
group
)
group
.
licensed_feature_available?
(
:group_ip_restriction
)
||
License
.
features_with_usage_ping
.
includ
e?
(
:group_ip_restriction
)
group
.
licensed_feature_available?
(
:group_ip_restriction
)
||
GitlabSubscriptions
::
Features
.
usage_ping_featur
e?
(
:group_ip_restriction
)
end
end
end
ee/app/helpers/admin/repo_size_limit_helper.rb
View file @
f3a8759a
...
...
@@ -3,7 +3,7 @@
module
Admin
module
RepoSizeLimitHelper
def
repo_size_limit_feature_available?
License
.
feature_available?
(
:repository_size_limit
)
||
License
.
features_with_usage_ping
.
includ
e?
(
:repository_size_limit
)
License
.
feature_available?
(
:repository_size_limit
)
||
GitlabSubscriptions
::
Features
.
usage_ping_featur
e?
(
:repository_size_limit
)
end
end
end
ee/app/models/ee/namespace.rb
View file @
f3a8759a
...
...
@@ -69,7 +69,7 @@ module EE
end
scope
:with_feature_available_in_plan
,
->
(
feature
)
do
plans
=
plans_with_feature
(
feature
)
plans
=
GitlabSubscriptions
::
Features
.
saas_
plans_with_feature
(
feature
)
matcher
=
::
Plan
.
where
(
name:
plans
)
.
joins
(
:hosted_subscriptions
)
.
where
(
"gitlab_subscriptions.namespace_id = namespaces.id"
)
...
...
@@ -140,14 +140,6 @@ module EE
limit
.
presence
||
build_namespace_limit
end
class_methods
do
extend
::
Gitlab
::
Utils
::
Override
def
plans_with_feature
(
feature
)
LICENSE_PLANS_TO_NAMESPACE_PLANS
.
values_at
(
*
License
.
plans_with_feature
(
feature
)).
flatten
end
end
override
:move_dir
def
move_dir
succeeded
=
super
...
...
@@ -186,7 +178,7 @@ module EE
def
feature_available_in_plan?
(
feature
)
available_features
=
strong_memoize
(
:features_available_in_plan
)
do
Hash
.
new
do
|
h
,
f
|
h
[
f
]
=
(
plans
.
map
(
&
:name
)
&
self
.
class
.
plans_with_feature
(
f
)).
any?
h
[
f
]
=
(
plans
.
map
(
&
:name
)
&
GitlabSubscriptions
::
Features
.
saas_
plans_with_feature
(
f
)).
any?
end
end
...
...
ee/app/models/ee/project.rb
View file @
f3a8759a
...
...
@@ -671,7 +671,7 @@ module EE
return
super
unless
License
.
current
License
.
current
.
features
.
select
do
|
feature
|
License
.
global_feature
?
(
feature
)
||
licensed_feature_available?
(
feature
)
GitlabSubscriptions
::
Features
.
global
?
(
feature
)
||
licensed_feature_available?
(
feature
)
end
end
...
...
ee/app/models/gitlab_subscriptions/features.rb
0 → 100644
View file @
f3a8759a
# frozen_string_literal: true
# All GitLab features that are available after purchasing a GitLab subscription
# either SaaS or self-managed.
# This class defines methods to check feature availability and their relation
# to GitLab plans.
module
GitlabSubscriptions
class
Features
# Global features that cannot be restricted to only a subset of projects or namespaces.
# Use `License.feature_available?(:feature)` to check if these features are available.
# For all other features, use `project.feature_available?` or `namespace.feature_available?` when possible.
GLOBAL_FEATURES
=
%i[
admin_audit_log
auditor_user
custom_file_templates
custom_project_templates
db_load_balancing
default_branch_protection_restriction_in_groups
elastic_search
enterprise_templates
extended_audit_events
external_authorization_service_api_management
geo
ldap_group_sync
ldap_group_sync_filter
multiple_ldap_servers
object_storage
pages_size_limit
project_aliases
repository_size_limit
required_ci_templates
seat_link
usage_quotas
]
.
freeze
STARTER_FEATURES
=
%i[
audit_events
blocked_issues
board_iteration_lists
code_owners
code_review_analytics
contribution_analytics
description_diffs
elastic_search
full_codequality_report
group_activity_analytics
group_bulk_edit
group_webhooks
issuable_default_templates
issue_weights
iterations
ldap_group_sync
member_lock
merge_request_approvers
milestone_charts
multiple_issue_assignees
multiple_ldap_servers
multiple_merge_request_assignees
multiple_merge_request_reviewers
project_merge_request_analytics
protected_refs_for_users
push_rules
repository_mirrors
resource_access_token
seat_link
scoped_issue_board
usage_quotas
visual_review_app
wip_limits
]
.
freeze
PREMIUM_FEATURES
=
%i[
adjourned_deletion_for_projects_and_groups
admin_audit_log
alert_metric_upload
auditor_user
blocking_merge_requests
board_assignee_lists
board_milestone_lists
ci_cd_projects
ci_secrets_management
cluster_agents_gitops
cluster_agents_ci_impersonation
cluster_deployments
code_owner_approval_required
commit_committer_check
compliance_framework
custom_compliance_frameworks
cross_project_pipelines
custom_file_templates
custom_file_templates_for_namespace
custom_project_templates
cycle_analytics_for_groups
cycle_analytics_for_projects
db_load_balancing
default_branch_protection_restriction_in_groups
default_project_deletion_protection
disable_name_update_for_users
email_additional_text
epics
extended_audit_events
external_authorization_service_api_management
feature_flags_related_issues
feature_flags_code_references
file_locks
geo
generic_alert_fingerprinting
git_two_factor_enforcement
github_project_service_integration
group_allowed_email_domains
group_coverage_reports
group_forking_protection
group_merge_request_analytics
group_milestone_project_releases
group_project_templates
group_repository_analytics
group_saml
group_saml_group_sync
group_scoped_ci_variables
group_wikis
incident_sla
incident_metric_upload
ide_schema_config
issues_analytics
jira_issues_integration
ldap_group_sync_filter
merge_pipelines
merge_request_performance_metrics
admin_merge_request_approvers_rules
merge_trains
metrics_reports
multiple_alert_http_integrations
multiple_approval_rules
multiple_group_issue_boards
multiple_iteration_cadences
object_storage
operations_dashboard
package_forwarding
pages_size_limit
productivity_analytics
project_aliases
protected_environments
reject_unsigned_commits
required_ci_templates
scoped_labels
smartcard_auth
swimlanes
type_of_work_analytics
minimal_access_role
unprotection_restrictions
ci_project_subscriptions
incident_timeline_view
oncall_schedules
escalation_policies
export_user_permissions
zentao_issues_integration
]
.
freeze
ULTIMATE_FEATURES
=
%i[
api_fuzzing
auto_rollback
cilium_alerts
cluster_image_scanning
external_status_checks
container_scanning
coverage_fuzzing
credentials_inventory
dast
dependency_scanning
devops_adoption
dora4_analytics
enforce_personal_access_token_expiration
enforce_ssh_key_expiration
enterprise_templates
environment_alerts
evaluate_group_level_compliance_pipeline
external_audit_events
group_ci_cd_analytics
group_level_compliance_dashboard
group_level_devops_adoption
incident_management
incident_timeline_events
inline_codequality
insights
instance_level_devops_adoption
issuable_health_status
jira_vulnerabilities_integration
jira_issue_association_enforcement
kubernetes_cluster_vulnerabilities
license_scanning
personal_access_token_expiration_policy
project_quality_summary
prometheus_alerts
pseudonymizer
quality_management
related_epics
release_evidence_test_artifacts
report_approver_rules
requirements
sast
sast_iac
sast_custom_rulesets
sast_fp_reduction
secret_detection
security_configuration_in_ui
security_dashboard
security_on_demand_scans
security_orchestration_policies
ssh_key_expiration_policy
status_page
subepics
threat_monitoring
vulnerability_auto_fix
vulnerability_finding_signatures
]
.
freeze
STARTER_FEATURES_WITH_USAGE_PING
=
%i[
send_emails_from_admin_area
repository_size_limit
]
.
freeze
PREMIUM_FEATURES_WITH_USAGE_PING
=
%i[
group_ip_restriction
]
.
freeze
ALL_STARTER_FEATURES
=
STARTER_FEATURES
+
STARTER_FEATURES_WITH_USAGE_PING
ALL_PREMIUM_FEATURES
=
ALL_STARTER_FEATURES
+
PREMIUM_FEATURES
+
PREMIUM_FEATURES_WITH_USAGE_PING
ALL_ULTIMATE_FEATURES
=
ALL_PREMIUM_FEATURES
+
ULTIMATE_FEATURES
ALL_FEATURES
=
ALL_ULTIMATE_FEATURES
FEATURES_WITH_USAGE_PING
=
STARTER_FEATURES_WITH_USAGE_PING
+
PREMIUM_FEATURES_WITH_USAGE_PING
FEATURES_BY_PLAN
=
{
License
::
STARTER_PLAN
=>
ALL_STARTER_FEATURES
,
License
::
PREMIUM_PLAN
=>
ALL_PREMIUM_FEATURES
,
License
::
ULTIMATE_PLAN
=>
ALL_ULTIMATE_FEATURES
}.
freeze
LICENSE_PLANS_TO_SAAS_PLANS
=
{
License
::
STARTER_PLAN
=>
[
::
Plan
::
BRONZE
],
License
::
PREMIUM_PLAN
=>
[
::
Plan
::
SILVER
,
::
Plan
::
PREMIUM
,
::
Plan
::
PREMIUM_TRIAL
],
License
::
ULTIMATE_PLAN
=>
[
::
Plan
::
GOLD
,
::
Plan
::
ULTIMATE
,
::
Plan
::
ULTIMATE_TRIAL
,
::
Plan
::
OPEN_SOURCE
]
}.
freeze
PLANS_BY_FEATURE
=
FEATURES_BY_PLAN
.
each_with_object
({})
do
|
(
plan
,
features
),
hash
|
features
.
each
do
|
feature
|
hash
[
feature
]
||=
[]
hash
[
feature
]
<<
plan
end
end
.
freeze
# Add on codes that may occur in legacy licenses that don't have a plan yet.
FEATURES_FOR_ADD_ONS
=
{
'GitLab_Auditor_User'
=>
:auditor_user
,
'GitLab_FileLocks'
=>
:file_locks
,
'GitLab_Geo'
=>
:geo
}.
freeze
class
<<
self
def
features
(
plan
:,
add_ons
:)
(
for_plan
(
plan
)
+
for_add_ons
(
add_ons
)).
to_set
end
def
global?
(
feature
)
GLOBAL_FEATURES
.
include?
(
feature
)
end
def
usage_ping_feature?
(
feature
)
features_with_usage_ping
.
include?
(
feature
)
end
def
plans_with_feature
(
feature
)
if
global?
(
feature
)
raise
ArgumentError
,
"Use `License.feature_available?` for features that cannot be restricted to only a subset of projects or namespaces"
end
PLANS_BY_FEATURE
.
fetch
(
feature
,
[])
end
def
saas_plans_with_feature
(
feature
)
LICENSE_PLANS_TO_SAAS_PLANS
.
values_at
(
*
plans_with_feature
(
feature
)).
flatten
end
private
def
features_with_usage_ping
return
FEATURES_WITH_USAGE_PING
if
Gitlab
::
CurrentSettings
.
usage_ping_features_enabled?
[]
end
def
for_plan
(
plan
)
FEATURES_BY_PLAN
.
fetch
(
plan
,
[])
end
def
for_add_ons
(
add_ons
)
add_ons
.
map
{
|
name
,
count
|
FEATURES_FOR_ADD_ONS
[
name
]
if
count
.
to_i
>
0
}.
compact
end
end
end
end
ee/app/models/license.rb
View file @
f3a8759a
# frozen_string_literal: true
# License is the artifact of purchasing a GitLab subscription for self-managed
# and it is installed at instance level.
# GitLab SaaS is a special self-managed instance which has a license installed
# that is mapped to an Ultimate plan.
class
License
<
ApplicationRecord
include
ActionView
::
Helpers
::
NumberHelper
include
Gitlab
::
Utils
::
StrongMemoize
...
...
@@ -17,247 +21,6 @@ class License < ApplicationRecord
EE_ALL_PLANS
=
[
STARTER_PLAN
,
PREMIUM_PLAN
,
ULTIMATE_PLAN
].
freeze
EES_FEATURES_WITH_USAGE_PING
=
%i[
send_emails_from_admin_area
repository_size_limit
]
.
freeze
EEP_FEATURES_WITH_USAGE_PING
=
%i[
group_ip_restriction
]
.
freeze
EES_FEATURES
=
%i[
audit_events
blocked_issues
board_iteration_lists
code_owners
code_review_analytics
contribution_analytics
description_diffs
elastic_search
full_codequality_report
group_activity_analytics
group_bulk_edit
group_webhooks
issuable_default_templates
issue_weights
iterations
ldap_group_sync
member_lock
merge_request_approvers
milestone_charts
multiple_issue_assignees
multiple_ldap_servers
multiple_merge_request_assignees
multiple_merge_request_reviewers
project_merge_request_analytics
protected_refs_for_users
push_rules
repository_mirrors
resource_access_token
seat_link
scoped_issue_board
usage_quotas
visual_review_app
wip_limits
]
.
freeze
+
EES_FEATURES_WITH_USAGE_PING
EEP_FEATURES
=
EES_FEATURES
+
EEP_FEATURES_WITH_USAGE_PING
+
%i[
adjourned_deletion_for_projects_and_groups
admin_audit_log
alert_metric_upload
auditor_user
blocking_merge_requests
board_assignee_lists
board_milestone_lists
ci_cd_projects
ci_secrets_management
cluster_agents_gitops
cluster_agents_ci_impersonation
cluster_deployments
code_owner_approval_required
commit_committer_check
compliance_framework
custom_compliance_frameworks
cross_project_pipelines
custom_file_templates
custom_file_templates_for_namespace
custom_project_templates
cycle_analytics_for_groups
cycle_analytics_for_projects
db_load_balancing
default_branch_protection_restriction_in_groups
default_project_deletion_protection
disable_name_update_for_users
email_additional_text
epics
extended_audit_events
external_authorization_service_api_management
feature_flags_related_issues
feature_flags_code_references
file_locks
geo
generic_alert_fingerprinting
git_two_factor_enforcement
github_project_service_integration
group_allowed_email_domains
group_coverage_reports
group_forking_protection
group_merge_request_analytics
group_milestone_project_releases
group_project_templates
group_repository_analytics
group_saml
group_saml_group_sync
group_scoped_ci_variables
group_wikis
incident_sla
incident_metric_upload
ide_schema_config
issues_analytics
jira_issues_integration
ldap_group_sync_filter
merge_pipelines
merge_request_performance_metrics
admin_merge_request_approvers_rules
merge_trains
metrics_reports
multiple_alert_http_integrations
multiple_approval_rules
multiple_group_issue_boards
multiple_iteration_cadences
object_storage
operations_dashboard
package_forwarding
pages_size_limit
productivity_analytics
project_aliases
protected_environments
reject_unsigned_commits
required_ci_templates
scoped_labels
smartcard_auth
swimlanes
type_of_work_analytics
minimal_access_role
unprotection_restrictions
ci_project_subscriptions
incident_timeline_view
oncall_schedules
escalation_policies
export_user_permissions
zentao_issues_integration
]
EEP_FEATURES
.
freeze
EEU_FEATURES
=
EEP_FEATURES
+
%i[
api_fuzzing
auto_rollback
cilium_alerts
cluster_image_scanning
external_status_checks
container_scanning
coverage_fuzzing
credentials_inventory
dast
dependency_scanning
devops_adoption
dora4_analytics
enforce_personal_access_token_expiration
enforce_ssh_key_expiration
enterprise_templates
environment_alerts
evaluate_group_level_compliance_pipeline
external_audit_events
group_ci_cd_analytics
group_level_compliance_dashboard
group_level_devops_adoption
incident_management
incident_timeline_events
inline_codequality
insights
instance_level_devops_adoption
issuable_health_status
jira_vulnerabilities_integration
jira_issue_association_enforcement
kubernetes_cluster_vulnerabilities
license_scanning
personal_access_token_expiration_policy
project_quality_summary
prometheus_alerts
pseudonymizer
quality_management
related_epics
release_evidence_test_artifacts
report_approver_rules
requirements
sast
sast_iac
sast_custom_rulesets
sast_fp_reduction
secret_detection
security_configuration_in_ui
security_dashboard
security_on_demand_scans
security_orchestration_policies
ssh_key_expiration_policy
status_page
subepics
threat_monitoring
vulnerability_auto_fix
vulnerability_finding_signatures
]
EEU_FEATURES
.
freeze
FEATURES_BY_PLAN
=
{
STARTER_PLAN
=>
EES_FEATURES
,
PREMIUM_PLAN
=>
EEP_FEATURES
,
ULTIMATE_PLAN
=>
EEU_FEATURES
}.
freeze
PLANS_BY_FEATURE
=
FEATURES_BY_PLAN
.
each_with_object
({})
do
|
(
plan
,
features
),
hash
|
features
.
each
do
|
feature
|
hash
[
feature
]
||=
[]
hash
[
feature
]
<<
plan
end
end
.
freeze
FEATURES_WITH_USAGE_PING
=
EES_FEATURES_WITH_USAGE_PING
+
EEP_FEATURES_WITH_USAGE_PING
# Add on codes that may occur in legacy licenses that don't have a plan yet.
FEATURES_FOR_ADD_ONS
=
{
'GitLab_Auditor_User'
=>
:auditor_user
,
'GitLab_FileLocks'
=>
:file_locks
,
'GitLab_Geo'
=>
:geo
}.
freeze
# Global features that cannot be restricted to only a subset of projects or namespaces.
# Use `License.feature_available?(:feature)` to check if these features are available.
# For all other features, use `project.feature_available?` or `namespace.feature_available?` when possible.
GLOBAL_FEATURES
=
%i[
admin_audit_log
auditor_user
custom_file_templates
custom_project_templates
db_load_balancing
default_branch_protection_restriction_in_groups
elastic_search
enterprise_templates
extended_audit_events
external_authorization_service_api_management
geo
ldap_group_sync
ldap_group_sync_filter
multiple_ldap_servers
object_storage
pages_size_limit
project_aliases
repository_size_limit
required_ci_templates
seat_link
usage_quotas
]
.
freeze
ACTIVE_USER_COUNT_THRESHOLD_LEVELS
=
[
{
range:
(
2
..
15
),
percentage:
false
,
value:
1
},
{
range:
(
16
..
25
),
percentage:
false
,
value:
2
},
...
...
@@ -287,28 +50,6 @@ class License < ApplicationRecord
CACHE_KEY
=
:current_license
class
<<
self
def
features_for_plan
(
plan
)
FEATURES_BY_PLAN
.
fetch
(
plan
,
[])
end
def
plans_with_feature
(
feature
)
if
global_feature?
(
feature
)
raise
ArgumentError
,
"Use `License.feature_available?` for features that cannot be restricted to only a subset of projects or namespaces"
end
PLANS_BY_FEATURE
.
fetch
(
feature
,
[])
end
def
features_with_usage_ping
return
FEATURES_WITH_USAGE_PING
if
Gitlab
::
CurrentSettings
.
usage_ping_features_enabled?
[]
end
def
plan_includes_feature?
(
plan
,
feature
)
plans_with_feature
(
feature
).
include?
(
plan
)
end
def
current
cache
.
fetch
(
CACHE_KEY
,
as:
License
,
expires_in:
1
.
minute
)
{
load_license
}
end
...
...
@@ -348,10 +89,6 @@ class License < ApplicationRecord
Gitlab
::
SafeRequestStore
.
delete
(
:future_dated_license
)
end
def
global_feature?
(
feature
)
GLOBAL_FEATURES
.
include?
(
feature
)
end
def
eligible_for_trial?
Gitlab
::
CurrentSettings
.
license_trial_ends_on
.
nil?
end
...
...
@@ -456,12 +193,8 @@ class License < ApplicationRecord
restricted_attr
(
:reconciliation_completed
)
end
def
features_from_add_ons
add_ons
.
map
{
|
name
,
count
|
FEATURES_FOR_ADD_ONS
[
name
]
if
count
.
to_i
>
0
}.
compact
end
def
features
@features
||=
(
self
.
class
.
features_for_plan
(
plan
)
+
features_from_add_ons
).
to_set
@features
||=
GitlabSubscriptions
::
Features
.
features
(
plan:
plan
,
add_ons:
add_ons
)
end
def
feature_available?
(
feature
)
...
...
ee/app/services/subscriptions/create_service.rb
→
ee/app/services/
gitlab_
subscriptions/create_service.rb
View file @
f3a8759a
# frozen_string_literal: true
module
Subscriptions
module
Gitlab
Subscriptions
class
CreateService
include
Gitlab
::
Utils
::
StrongMemoize
...
...
ee/lib/ee/api/features.rb
View file @
f3a8759a
...
...
@@ -13,7 +13,7 @@ module EE
def
validate_feature_flag_name!
(
name
)
super
if
License
::
PLANS_BY_FEATURE
[
name
.
to_sym
]
if
GitlabSubscriptions
::
Features
::
PLANS_BY_FEATURE
[
name
.
to_sym
]
bad_request!
(
"The '
#{
name
}
' is a licensed feature name, "
\
"and thus it cannot be used as a feature flag name. "
\
...
...
ee/lib/gitlab/ip_restriction/enforcer.rb
View file @
f3a8759a
...
...
@@ -12,7 +12,7 @@ module Gitlab
end
def
allows_current_ip?
return
true
unless
group
&
.
feature_available?
(
:group_ip_restriction
)
||
::
License
.
features_with_usage_ping
.
includ
e?
(
:group_ip_restriction
)
return
true
unless
group
&
.
feature_available?
(
:group_ip_restriction
)
||
::
GitlabSubscriptions
::
Features
.
usage_ping_featur
e?
(
:group_ip_restriction
)
current_ip_address
=
Gitlab
::
IpAddressState
.
current
...
...
ee/spec/controllers/subscriptions_controller_spec.rb
View file @
f3a8759a
...
...
@@ -273,7 +273,7 @@ RSpec.describe SubscriptionsController do
before
do
sign_in
(
user
)
allow_any_instance_of
(
Subscriptions
::
CreateService
).
to
receive
(
:execute
).
and_return
(
service_response
)
allow_any_instance_of
(
Gitlab
Subscriptions
::
CreateService
).
to
receive
(
:execute
).
and_return
(
service_response
)
allow_any_instance_of
(
EE
::
Groups
::
CreateService
).
to
receive
(
:execute
).
and_return
(
group
)
end
...
...
ee/spec/lib/ee/gitlab/gon_helper_spec.rb
View file @
f3a8759a
...
...
@@ -42,7 +42,7 @@ RSpec.describe EE::Gitlab::GonHelper do
end
describe
'#push_licensed_feature'
do
let_it_be
(
:feature
)
{
License
::
EEU
_FEATURES
.
first
}
let_it_be
(
:feature
)
{
GitlabSubscriptions
::
Features
::
ALL
_FEATURES
.
first
}
shared_examples
'sets the licensed features flag'
do
it
'pushes the licensed feature flag to the frotnend'
do
...
...
ee/spec/models/gitlab_subscriptions/features_spec.rb
0 → 100644
View file @
f3a8759a
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
GitlabSubscriptions
::
Features
do
describe
'License -> Plan mapping'
do
(
::
Plan
.
all_plans
-
::
Plan
.
default_plans
).
each
do
|
plan_name
|
describe
"
#{
plan_name
}
plan"
do
it
'is mapped to a license tier'
do
expect
(
described_class
::
LICENSE_PLANS_TO_SAAS_PLANS
.
values
.
flatten
).
to
include
(
plan_name
)
end
end
end
end
describe
'.plans_with_feature'
do
subject
{
described_class
.
plans_with_feature
(
feature
)
}
context
'when params is a Starter feature'
do
let
(
:feature
)
{
described_class
::
STARTER_FEATURES
.
sample
}
it
{
is_expected
.
to
contain_exactly
(
License
::
STARTER_PLAN
,
License
::
PREMIUM_PLAN
,
License
::
ULTIMATE_PLAN
)
}
end
context
'when params is a Premium feature'
do
let
(
:feature
)
{
described_class
::
PREMIUM_FEATURES
.
sample
}
it
{
is_expected
.
to
contain_exactly
(
License
::
PREMIUM_PLAN
,
License
::
ULTIMATE_PLAN
)
}
end
context
'when params is a Ultimate feature'
do
let
(
:feature
)
{
described_class
::
ULTIMATE_FEATURES
.
sample
}
it
{
is_expected
.
to
contain_exactly
(
License
::
ULTIMATE_PLAN
)
}
end
context
'when param is a global feature'
do
let
(
:feature
)
{
described_class
::
GLOBAL_FEATURES
.
sample
}
it
{
expect
{
subject
}.
to
raise_error
(
ArgumentError
)
}
end
context
'when feature does not exist'
do
let
(
:feature
)
{
:does_not_exist
}
it
{
is_expected
.
to
be_empty
}
end
end
describe
'.saas_plans_with_feature'
do
subject
{
described_class
.
saas_plans_with_feature
(
feature
)
}
context
'a Starter feature'
do
let
(
:feature
)
{
:audit_events
}
it
'is present in all paid plans'
do
expect
(
subject
).
to
contain_exactly
(
*::
Plan
::
PAID_HOSTED_PLANS
)
end
end
context
'a Premium feature'
do
let
(
:feature
)
{
:epics
}
it
'is present in all Premium+ plans'
do
expected_plans
=
::
Plan
::
PAID_HOSTED_PLANS
-
[
::
Plan
::
BRONZE
]
expect
(
subject
).
to
contain_exactly
(
*
expected_plans
)
end
end
context
'an Ultimate feature'
do
let
(
:feature
)
{
:dast
}
it
'is present in all top plans'
do
expected_plans
=
::
Plan
::
PAID_HOSTED_PLANS
-
[
::
Plan
::
BRONZE
,
::
Plan
::
SILVER
,
::
Plan
::
PREMIUM
,
::
Plan
::
PREMIUM_TRIAL
]
expect
(
subject
).
to
contain_exactly
(
*
expected_plans
)
end
end
context
'a global feature'
do
let
(
:feature
)
{
:elastic_search
}
it
'cannot be checked using this method'
do
expect
{
subject
}.
to
raise_error
(
ArgumentError
)
end
end
context
'a non existing feature'
do
let
(
:feature
)
{
:unknown_feature
}
it
'is not in any plan'
do
expect
(
subject
).
to
be_empty
end
end
end
describe
'.global?'
do
subject
{
described_class
.
global?
(
feature
)
}
context
'when it is a global feature'
do
let
(
:feature
)
{
:geo
}
it
{
is_expected
.
to
be
(
true
)
}
end
context
'when it is not a global feature'
do
let
(
:feature
)
{
:sast
}
it
{
is_expected
.
to
be
(
false
)
}
end
end
describe
'.features'
do
subject
(
:features
)
{
described_class
.
features
(
plan:
plan
,
add_ons:
add_ons
)
}
let
(
:add_ons
)
{
{}
}
let_it_be
(
:starter_feature
)
{
described_class
::
STARTER_FEATURES
.
sample
}
let_it_be
(
:premium_feature
)
{
described_class
::
PREMIUM_FEATURES
.
sample
}
let_it_be
(
:ultimate_feature
)
{
described_class
::
ULTIMATE_FEATURES
.
sample
}
let_it_be
(
:add_on_feature
)
{
:geo
}
let_it_be
(
:geo_addon
)
{
{
'GitLab_Geo'
=>
1
}
}
context
'when plan is Starter'
do
let
(
:plan
)
{
License
::
STARTER_PLAN
}
it
'includes only Starter features'
do
expect
(
features
).
to
include
(
starter_feature
)
expect
(
features
).
not_to
include
(
premium_feature
)
expect
(
features
).
not_to
include
(
ultimate_feature
)
end
context
'when add-ons are present'
do
let
(
:add_ons
)
{
geo_addon
}
it
'includes only Starter features'
do
expect
(
features
).
to
include
(
starter_feature
)
expect
(
features
).
not_to
include
(
premium_feature
)
expect
(
features
).
not_to
include
(
ultimate_feature
)
end
it
'includes also add-on features'
do
expect
(
features
).
to
include
(
add_on_feature
)
end
end
end
context
'when plan is Premium'
do
let
(
:plan
)
{
License
::
PREMIUM_PLAN
}
it
'includes Starter and Premium features'
do
expect
(
features
).
to
include
(
starter_feature
)
expect
(
features
).
to
include
(
premium_feature
)
expect
(
features
).
not_to
include
(
ultimate_feature
)
end
context
'when add-ons are present'
do
let
(
:add_ons
)
{
geo_addon
}
it
'includes Starter and Premium features'
do
expect
(
features
).
to
include
(
starter_feature
)
expect
(
features
).
to
include
(
premium_feature
)
expect
(
features
).
not_to
include
(
ultimate_feature
)
end
it
'includes also add-on features'
do
expect
(
features
).
to
include
(
add_on_feature
)
end
end
end
context
'when plan is Ultimate'
do
let
(
:plan
)
{
License
::
ULTIMATE_PLAN
}
it
'includes Starter, Premium and Ultimate features'
do
expect
(
features
).
to
include
(
starter_feature
)
expect
(
features
).
to
include
(
premium_feature
)
expect
(
features
).
to
include
(
ultimate_feature
)
end
context
'when add-ons are present'
do
let
(
:add_ons
)
{
geo_addon
}
it
'includes Starter, Premium and Ultimate features'
do
expect
(
features
).
to
include
(
starter_feature
)
expect
(
features
).
to
include
(
premium_feature
)
expect
(
features
).
to
include
(
ultimate_feature
)
end
it
'includes also add-on features'
do
expect
(
features
).
to
include
(
add_on_feature
)
end
end
end
end
describe
'.usage_ping_feature?'
do
subject
{
described_class
.
usage_ping_feature?
(
feature
)
}
let
(
:usage_ping_enabled
)
{
true
}
before
do
stub_application_setting
(
usage_ping_features_enabled:
usage_ping_enabled
)
end
context
'when param is a Starter usage ping feature'
do
let
(
:feature
)
{
described_class
::
STARTER_FEATURES_WITH_USAGE_PING
.
sample
}
it
{
is_expected
.
to
be_truthy
}
context
'when usage ping setting is disabled'
do
let
(
:usage_ping_enabled
)
{
false
}
it
{
is_expected
.
to
be_falsey
}
end
end
context
'when param is a Premium usage ping feature'
do
let
(
:feature
)
{
described_class
::
PREMIUM_FEATURES_WITH_USAGE_PING
.
sample
}
it
{
is_expected
.
to
be_truthy
}
context
'when usage ping setting is disabled'
do
let
(
:usage_ping_enabled
)
{
false
}
it
{
is_expected
.
to
be_falsey
}
end
end
context
'when param is another usage ping feature'
do
let
(
:feature
)
{
:audit_events
}
it
{
is_expected
.
to
be_falsey
}
end
end
end
ee/spec/models/license_spec.rb
View file @
f3a8759a
...
...
@@ -547,53 +547,6 @@ RSpec.describe License do
described_class
.
reset_current
end
describe
'.features_for_plan'
do
it
'returns features for starter plan'
do
expect
(
described_class
.
features_for_plan
(
'starter'
))
.
to
include
(
:multiple_issue_assignees
)
end
it
'returns features for premium plan'
do
expect
(
described_class
.
features_for_plan
(
'premium'
))
.
to
include
(
:multiple_issue_assignees
,
:cluster_deployments
,
:file_locks
,
:group_wikis
)
end
it
'returns empty array if no features for given plan'
do
expect
(
described_class
.
features_for_plan
(
'bronze'
)).
to
eq
([])
end
end
describe
'.plan_includes_feature?'
do
let
(
:feature
)
{
:cluster_deployments
}
subject
{
described_class
.
plan_includes_feature?
(
plan
,
feature
)
}
context
'when addon included'
do
let
(
:plan
)
{
'premium'
}
it
{
is_expected
.
to
eq
(
true
)
}
end
context
'when addon not included'
do
let
(
:plan
)
{
'starter'
}
it
{
is_expected
.
to
eq
(
false
)
}
end
context
'when plan is not set'
do
let
(
:plan
)
{
nil
}
it
{
is_expected
.
to
eq
(
false
)
}
end
context
'when feature does not exists'
do
let
(
:plan
)
{
'premium'
}
let
(
:feature
)
{
nil
}
it
{
is_expected
.
to
eq
(
false
)
}
end
end
describe
'.current'
,
:request_store
,
:use_clean_rails_memory_store_caching
do
context
'when licenses table does not exist'
do
it
'returns nil'
do
...
...
@@ -733,22 +686,6 @@ RSpec.describe License do
end
end
describe
'.global_feature?'
do
subject
{
described_class
.
global_feature?
(
feature
)
}
context
'when it is a global feature'
do
let
(
:feature
)
{
:geo
}
it
{
is_expected
.
to
be
(
true
)
}
end
context
'when it is not a global feature'
do
let
(
:feature
)
{
:sast
}
it
{
is_expected
.
to
be
(
false
)
}
end
end
describe
'.with_valid_license'
do
context
'when license trial'
do
before
do
...
...
@@ -925,32 +862,6 @@ RSpec.describe License do
end
end
describe
'#features_from_add_ons'
do
context
'without add-ons'
do
it
'returns an empty array'
do
license
=
build_license_with_add_ons
({},
plan:
'unknown'
)
expect
(
license
.
features_from_add_ons
).
to
eq
([])
end
end
context
'with add-ons'
do
it
'returns all available add-ons'
do
license
=
build_license_with_add_ons
({
'GitLab_FileLocks'
=>
2
})
expect
(
license
.
features_from_add_ons
).
to
eq
([
:file_locks
])
end
end
context
'with nil add-ons'
do
it
'returns an empty array'
do
license
=
build_license_with_add_ons
({
'GitLab_FileLocks'
=>
nil
})
expect
(
license
.
features_from_add_ons
).
to
eq
([])
end
end
end
describe
'#feature_available?'
do
it
'returns true if add-on exists and have a quantity greater than 0'
do
license
=
build_license_with_add_ons
({
'GitLab_FileLocks'
=>
1
})
...
...
@@ -983,7 +894,7 @@ RSpec.describe License do
described_class
.
delete_all
end
::
License
::
EES
_FEATURES
.
each
do
|
feature
|
::
GitlabSubscriptions
::
Features
::
ALL_STARTER
_FEATURES
.
each
do
|
feature
|
it
"returns false for
#{
feature
}
"
do
expect
(
license
.
feature_available?
(
feature
)).
to
eq
(
false
)
end
...
...
ee/spec/models/project_spec.rb
View file @
f3a8759a
...
...
@@ -1093,11 +1093,11 @@ RSpec.describe Project do
allow
(
namespace
).
to
receive
(
:plan
)
{
plan_license
}
end
License
::
EEU
_FEATURES
.
each
do
|
feature_sym
|
GitlabSubscriptions
::
Features
::
ALL
_FEATURES
.
each
do
|
feature_sym
|
context
feature_sym
.
to_s
do
let
(
:feature
)
{
feature_sym
}
unless
License
::
GLOBAL_FEATURES
.
include?
(
feature_sym
)
unless
GitlabSubscriptions
::
Features
::
GLOBAL_FEATURES
.
include?
(
feature_sym
)
context
"checking
#{
feature_sym
}
availability both on Global and Namespace license"
do
let
(
:check_namespace_plan
)
{
true
}
...
...
@@ -1126,7 +1126,7 @@ RSpec.describe Project do
end
end
unless
License
.
plan_includes_feature?
(
License
::
STARTER_PLAN
,
feature_sym
)
unless
GitlabSubscriptions
::
Features
.
plans_with_feature
(
feature_sym
).
include?
(
License
::
STARTER_PLAN
)
context
'not allowed by Plan License'
do
let
(
:allowed_on_global_license
)
{
true
}
let
(
:plan_license
)
{
build
(
:bronze_plan
)
}
...
...
ee/spec/requests/api/features_spec.rb
View file @
f3a8759a
...
...
@@ -38,7 +38,7 @@ RSpec.describe API::Features, stub_feature_flags: false do
context
'when licensed feature name is given'
do
let
(
:feature_name
)
do
License
::
PLANS_BY_FEATURE
.
each_key
.
first
GitlabSubscriptions
::
Features
::
PLANS_BY_FEATURE
.
each_key
.
first
end
it
'returns bad request'
do
...
...
ee/spec/services/subscriptions/create_service_spec.rb
→
ee/spec/services/
gitlab_
subscriptions/create_service_spec.rb
View file @
f3a8759a
...
...
@@ -2,7 +2,7 @@
require
'spec_helper'
RSpec
.
describe
Subscriptions
::
CreateService
do
RSpec
.
describe
Gitlab
Subscriptions
::
CreateService
do
subject
(
:execute
)
{
described_class
.
new
(
user
,
group:
group
,
customer_params:
customer_params
,
subscription_params:
subscription_params
).
execute
}
let_it_be
(
:user
)
{
create
(
:user
,
id:
111
,
first_name:
'First name'
,
last_name:
'Last name'
,
email:
'first.last@gitlab.com'
)
}
...
...
ee/spec/support/helpers/ee/license_helpers.rb
View file @
f3a8759a
...
...
@@ -15,8 +15,7 @@ module EE
prepended
do
def
stub_licensed_features
(
features
)
# EEU_FEATURES contains all the features we know about
missing_features
=
features
.
keys
.
map
(
&
:to_sym
)
-
License
::
EEU_FEATURES
missing_features
=
features
.
keys
.
map
(
&
:to_sym
)
-
GitlabSubscriptions
::
Features
::
ALL_FEATURES
if
missing_features
.
any?
subject
=
missing_features
.
join
(
', '
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment