Skip to content
Snippets Groups Projects
Commit bb2a0453 authored by Douwe Maan's avatar Douwe Maan
Browse files

Merge branch 'license-plan-check-on-namespaces' into 'master'

License plan check on namespaces

See merge request !1961
parents e72964c7 6c25cadf
No related branches found
No related tags found
3 merge requests!2122Merge RC2 into 9.3 (actually-stable),!2106Add RC2 changes to 9-3-stable-ee,!1961License plan check on namespaces
Pipeline #
Showing
with 159 additions and 25 deletions
Loading
Loading
@@ -174,7 +174,8 @@ def application_setting_params_ee
:shared_runners_minutes,
:minimum_mirror_sync_time,
:geo_status_timeout,
:elasticsearch_experimental_indexer
:elasticsearch_experimental_indexer,
:check_namespace_plan
]
end
end
Loading
Loading
@@ -85,7 +85,8 @@ def group_params_ce
def group_params_ee
[
:repository_size_limit,
:shared_runners_minutes_limit
:shared_runners_minutes_limit,
:plan
]
end
end
Loading
Loading
@@ -206,7 +206,7 @@ def user_params_ce
def user_params_ee
[
:note,
namespace_attributes: [:id, :shared_runners_minutes_limit]
namespace_attributes: [:id, :shared_runners_minutes_limit, :plan]
]
end
end
Loading
Loading
@@ -45,7 +45,7 @@ def destroy
private
 
def check_license
unless license_allows_file_locks?
unless @project.feature_available?(:file_lock)
flash[:alert] = 'You need a different license to enable FileLocks feature'
redirect_to admin_license_path
end
Loading
Loading
Loading
Loading
@@ -52,7 +52,7 @@ def logs_tree
contents.push(*tree.blobs)
contents.push(*tree.submodules)
 
show_path_locks = license_allows_file_locks? && @project.path_locks.any?
show_path_locks = @project.feature_available?(:file_lock) && @project.path_locks.any?
 
@logs = contents[@offset, @limit].to_a.map do |content|
file = @path ? File.join(@path, content.name) : content.name
Loading
Loading
module AuditorUserHelper
def license_allows_auditor_user?
@license_allows_auditor_user ||= (::License.current && ::License.current.add_on?('GitLab_Auditor_User'))
@license_allows_auditor_user ||= (::License.current&.feature_available?(:auditor_user))
end
end
Loading
Loading
@@ -3,10 +3,6 @@ def can_unlock?(path_lock, current_user = @current_user, project = @project)
can?(current_user, :admin_path_locks, project) || path_lock.user == current_user
end
 
def license_allows_file_locks?
@license_allows_file_locks ||= (::License.current && ::License.current.add_on?('GitLab_FileLocks'))
end
def text_label_for_lock(file_lock, path)
if file_lock.path == path
"Locked by #{file_lock.user.name}"
Loading
Loading
Loading
Loading
@@ -109,7 +109,7 @@ def flatten_tree(tree)
end
 
def lock_file_link(project = @project, path = @path, html_options: {})
return unless license_allows_file_locks? && current_user
return unless project.feature_available?(:file_lock) && current_user
return if path.blank?
 
path_lock = project.find_path_lock(path, downstream: true)
Loading
Loading
@@ -169,7 +169,7 @@ def enabled_lock_link(label, title, html_options)
end
 
def render_lock_icon(path)
return unless license_allows_file_locks?
return unless @project.feature_available?(:file_lock)
return unless @project.root_ref?(@ref)
 
if file_lock = @project.find_path_lock(path, exact_match: true)
Loading
Loading
Loading
Loading
@@ -10,5 +10,9 @@ module ApplicationSetting
validates :shared_runners_minutes,
numericality: { greater_than_or_equal_to: 0 }
end
def should_check_namespace_plan?
check_namespace_plan? && (::Gitlab.com? || Rails.env.development?)
end
end
end
Loading
Loading
@@ -6,11 +6,36 @@ module EE
module Namespace
extend ActiveSupport::Concern
 
BRONZE_PLAN = 'bronze'.freeze
SILVER_PLAN = 'silver'.freeze
GOLD_PLAN = 'gold'.freeze
EARLY_ADOPTER_PLAN = 'early_adopter'.freeze
EE_PLANS = {
BRONZE_PLAN => License::STARTER_PLAN,
SILVER_PLAN => License::PREMIUM_PLAN,
GOLD_PLAN => License::ULTIMATE_PLAN,
EARLY_ADOPTER_PLAN => License::EARLY_ADOPTER_PLAN
}.freeze
prepended do
has_one :namespace_statistics, dependent: :destroy
 
delegate :shared_runners_minutes, :shared_runners_seconds, :shared_runners_seconds_last_reset,
to: :namespace_statistics, allow_nil: true
validates :plan, inclusion: { in: EE_PLANS.keys }, allow_nil: true
end
# Checks features (i.e. https://about.gitlab.com/products/) availabily
# for a given Namespace plan. This method should consider ancestor groups
# being licensed.
def feature_available?(feature)
@features_available ||= Hash.new do |h, feature|
h[feature] = plans.any? { |plan| License.plan_includes_feature?(EE_PLANS[plan], feature) }
end
@features_available[feature]
end
 
def actual_shared_runners_minutes_limit
Loading
Loading
@@ -27,5 +52,16 @@ def shared_runners_minutes_used?
shared_runners_minutes_limit_enabled? &&
shared_runners_minutes.to_i >= actual_shared_runners_minutes_limit
end
private
def plans
@ancestors_plans ||=
if parent_id
ancestors.where.not(plan: nil).reorder(nil).pluck('DISTINCT plan') + [plan]
else
[plan]
end
end
end
end
Loading
Loading
@@ -24,6 +24,17 @@ def shared_runners_minutes_limit_enabled?
!public? && shared_runners_enabled? && namespace.shared_runners_minutes_limit_enabled?
end
 
# Checks licensed feature availability if `feature` matches any
# key on License::FEATURE_CODES. Otherwise, check feature availability
# through ProjectFeature.
def feature_available?(feature, user = nil)
if License::FEATURE_CODES.key?(feature)
licensed_feature_available?(feature)
else
super
end
end
def service_desk_address
return nil unless service_desk_available?
 
Loading
Loading
@@ -35,6 +46,17 @@ def service_desk_address
 
private
 
def licensed_feature_available?(feature)
globally_available = License.current&.feature_available?(feature)
if current_application_settings.should_check_namespace_plan?
globally_available &&
(public? && namespace.public? || namespace.feature_available?(feature))
else
globally_available
end
end
def service_desk_available?
return @service_desk_available if defined?(@service_desk_available)
 
Loading
Loading
class License < ActiveRecord::Base
include ActionView::Helpers::NumberHelper
 
DEPLOY_BOARD_FEATURE = 'GitLab_DeployBoard'.freeze
FILE_LOCK_FEATURE = 'GitLab_FileLocks'.freeze
GEO_FEATURE = 'GitLab_Geo'.freeze
AUDITOR_USER_FEATURE = 'GitLab_Auditor_User'.freeze
SERVICE_DESK_FEATURE = 'GitLab_ServiceDesk'.freeze
FEATURE_CODES = {
geo: GEO_FEATURE,
auditor_user: AUDITOR_USER_FEATURE,
service_desk: SERVICE_DESK_FEATURE,
# Features that make sense to Namespace:
deploy_board: DEPLOY_BOARD_FEATURE,
file_lock: FILE_LOCK_FEATURE
}.freeze
STARTER_PLAN = 'starter'.freeze
PREMIUM_PLAN = 'premium'.freeze
ULTIMATE_PLAN = 'ultimate'.freeze
EARLY_ADOPTER_PLAN = 'early_adopter'.freeze
EES_FEATURES = [
# ..
].freeze
 
EEP_FEATURES = [
*EES_FEATURES,
{ 'GitLab_DeployBoard' => 1 },
{ 'GitLab_FileLocks' => 1 },
{ 'GitLab_Geo' => 1 },
{ 'GitLab_Auditor_User' => 1 },
{ 'GitLab_ServiceDesk' => 1 }
{ DEPLOY_BOARD_FEATURE => 1 },
{ FILE_LOCK_FEATURE => 1 },
{ GEO_FEATURE => 1 },
{ AUDITOR_USER_FEATURE => 1 },
{ SERVICE_DESK_FEATURE => 1 }
].freeze
EEU_FEATURES = [
*EEP_FEATURES
# ..
].freeze
# List all features available for early adopters,
# i.e. users that started using GitLab.com before
# the introduction of Bronze, Silver, Gold plans.
# Obs.: Do not extend from other feature constants.
# Early adopters should not earn new features as they're
# introduced.
EARLY_ADOPTER_FEATURES = [
# TODO: Add EES features
# https://gitlab.com/gitlab-org/gitlab-ee/issues/2335)
{ DEPLOY_BOARD_FEATURE => 1 },
{ FILE_LOCK_FEATURE => 1 },
{ GEO_FEATURE => 1 },
{ AUDITOR_USER_FEATURE => 1 },
{ SERVICE_DESK_FEATURE => 1 }
].freeze
 
FEATURES_BY_PLAN = {
'starter' => EES_FEATURES,
'premium' => EEP_FEATURES
STARTER_PLAN => EES_FEATURES,
PREMIUM_PLAN => EEP_FEATURES,
ULTIMATE_PLAN => EEU_FEATURES,
EARLY_ADOPTER_PLAN => EARLY_ADOPTER_FEATURES
}.freeze
 
validate :valid_license
Loading
Loading
@@ -48,6 +91,13 @@ def reset_current
RequestStore.delete(:current_license)
end
 
def plan_includes_feature?(plan, code)
features = features_for_plan(plan)
feature = FEATURE_CODES.fetch(code)
features[feature].to_i > 0
end
def block_changes?
!current || current.block_changes?
end
Loading
Loading
@@ -115,8 +165,9 @@ def add_ons
explicit_add_ons.merge(plan_features)
end
 
def add_on?(code)
add_ons[code].to_i > 0
def feature_available?(code)
feature = FEATURE_CODES.fetch(code)
add_ons[feature].to_i > 0
end
 
def restricted_user_count
Loading
Loading
Loading
Loading
@@ -76,7 +76,7 @@ def reporter_access!
can! :read_deployment
can! :read_merge_request
 
if License.current&.add_on?('GitLab_DeployBoard') || Rails.env.development?
if project.feature_available?(:deploy_board) || Rails.env.development?
can! :read_deploy_board
end
end
Loading
Loading
.form-group
= f.label :plan, class: 'control-label'
.col-sm-10
= f.select :plan, options_for_select(Namespace::EE_PLANS.keys.map { |plan| [plan.titleize, plan] }, f.object.plan), {}, class: 'form-control'
Loading
Loading
@@ -95,6 +95,16 @@
= f.check_box :user_default_external
Newly registered users will by default be external
 
- if Gitlab.com? || Rails.env.development?
.form-group
= f.label :check_namespace_plan, 'Check feature availability on namespace plan', class: 'control-label col-sm-2'
.col-sm-10
.checkbox
= f.label :check_namespace_plan do
= f.check_box :check_namespace_plan
Enabling this will only make licensed EE features available to projects if the project namespace's plan
includes the feature or if the project is public.
%fieldset
%legend Sign-up Restrictions
.form-group
Loading
Loading
Loading
Loading
@@ -4,6 +4,9 @@
 
= render 'groups/repository_size_limit_setting', f: f
 
- if current_application_settings.should_check_namespace_plan?
= render 'admin/namespace_plan', f: f
.form-group.group-description-holder
= f.label :avatar, "Group avatar", class: 'control-label'
.col-sm-10
Loading
Loading
Loading
Loading
@@ -70,6 +70,12 @@
= f.label :note, 'Note', class: 'control-label'
.col-sm-10= f.text_area :note, class: 'form-control'
 
- if current_application_settings.should_check_namespace_plan?
= f.fields_for :namespace do |namespace_form|
%fieldset
%legend Plan
= render 'admin/namespace_plan', f: namespace_form
.form-actions
- if @user.new_record?
= f.submit 'Create user', class: "btn btn-create"
Loading
Loading
Loading
Loading
@@ -20,7 +20,7 @@
 
= render 'projects/fork_suggestion'
 
- if license_allows_file_locks?
- if @project.feature_available?(:file_lock)
:javascript
PathLocks.init(
'#{toggle_namespace_project_path_locks_path(@project.namespace, @project)}',
Loading
Loading
Loading
Loading
@@ -35,7 +35,7 @@
= link_to charts_namespace_project_graph_path(@project.namespace, @project, current_ref) do
Charts
 
- if license_allows_file_locks?
- if @project.feature_available?(:file_lock)
= nav_link(controller: [:path_locks]) do
= link_to namespace_project_path_locks_path(@project.namespace, @project) do
Locked Files
Loading
Loading
@@ -23,7 +23,7 @@
= render 'projects/blob/upload', title: 'Upload New File', placeholder: 'Upload new file', button_title: 'Upload file', form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post
= render 'projects/blob/new_dir'
 
- if license_allows_file_locks?
- if @project.feature_available?(:file_lock)
:javascript
PathLocks.init(
'#{toggle_namespace_project_path_locks_path(@project.namespace, @project)}',
Loading
Loading
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