1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
# frozen_string_literal: true
class ProjectFeature < ApplicationRecord
include Featurable
# When updating this array, make sure to update rubocop/cop/gitlab/feature_available_usage.rb as well.
FEATURES = %i[
issues
forking
merge_requests
wiki
snippets
builds
repository
pages
metrics_dashboard
analytics
operations
security_and_compliance
container_registry
].freeze
EXPORTABLE_FEATURES = (FEATURES - [:security_and_compliance, :pages]).freeze
set_available_features(FEATURES)
PRIVATE_FEATURES_MIN_ACCESS_LEVEL = { merge_requests: Gitlab::Access::REPORTER, metrics_dashboard: Gitlab::Access::REPORTER }.freeze
PRIVATE_FEATURES_MIN_ACCESS_LEVEL_FOR_PRIVATE_PROJECT = { repository: Gitlab::Access::REPORTER }.freeze
class << self
def required_minimum_access_level(feature)
feature = ensure_feature!(feature)
PRIVATE_FEATURES_MIN_ACCESS_LEVEL.fetch(feature, Gitlab::Access::GUEST)
end
# Guest users can perform certain features on public and internal projects, but not private projects.
def required_minimum_access_level_for_private_project(feature)
feature = ensure_feature!(feature)
PRIVATE_FEATURES_MIN_ACCESS_LEVEL_FOR_PRIVATE_PROJECT.fetch(feature) do
required_minimum_access_level(feature)
end
end
end
before_create :set_container_registry_access_level
# Default scopes force us to unscope here since a service may need to check
# permissions for a project in pending_delete
# http://stackoverflow.com/questions/1540645/how-to-disable-default-scope-for-a-belongs-to
belongs_to :project, -> { unscope(where: :pending_delete) }
validates :project, presence: true
validate :repository_children_level
validate :allowed_access_levels
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 :forking_access_level, value: ENABLED, allows_nil: false
default_value_for :merge_requests_access_level, value: ENABLED, allows_nil: false
default_value_for :snippets_access_level, value: ENABLED, allows_nil: false
default_value_for :wiki_access_level, value: ENABLED, allows_nil: false
default_value_for :repository_access_level, value: ENABLED, allows_nil: false
default_value_for :analytics_access_level, value: ENABLED, allows_nil: false
default_value_for :metrics_dashboard_access_level, value: PRIVATE, allows_nil: false
default_value_for :operations_access_level, value: ENABLED, allows_nil: false
default_value_for :security_and_compliance_access_level, value: PRIVATE, allows_nil: false
default_value_for(:pages_access_level, allows_nil: false) do |feature|
if ::Gitlab::Pages.access_control_is_forced?
PRIVATE
else
feature.project&.public? ? ENABLED : PRIVATE
end
end
def public_pages?
return true unless Gitlab.config.pages.access_control
return false if ::Gitlab::Pages.access_control_is_forced?
pages_access_level == PUBLIC || pages_access_level == ENABLED && project.public?
end
def private_pages?
!public_pages?
end
private
def set_container_registry_access_level
self.container_registry_access_level =
if project&.container_registry_enabled
ENABLED
else
DISABLED
end
end
# Validates builds and merge requests access level
# which cannot be higher than repository access level
def repository_children_level
validator = lambda do |field|
level = public_send(field) || ENABLED # rubocop:disable GitlabSecurity/PublicSend
not_allowed = level > repository_access_level
self.errors.add(field, "cannot have higher visibility level than repository access level") if not_allowed
end
%i(merge_requests_access_level builds_access_level).each(&validator)
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)
case access_level(feature)
when DISABLED
false
when PRIVATE
team_access?(user, feature)
when ENABLED
true
when PUBLIC
true
else
true
end
end
def team_access?(user, feature)
return unless user
return true if user.can_read_all_resources?
project.team.member?(user, ProjectFeature.required_minimum_access_level(feature))
end
end
ProjectFeature.prepend_if_ee('EE::ProjectFeature')
|