ability.rb 11.2 KB
Newer Older
G
gitlabhq 已提交
1
class Ability
A
Andrey Kumanyaev 已提交
2
  class << self
3
    def allowed(user, subject)
4
      return anonymous_abilities(user, subject) if user.nil?
D
Douwe Maan 已提交
5
      return [] unless user.is_a?(User)
6
      return [] if user.blocked?
7

8 9 10 11
      case subject
      when CommitStatus then commit_status_abilities(user, subject)
      when Project then project_abilities(user, subject)
      when Issue then issue_abilities(user, subject)
12
      when ExternalIssue then external_issue_abilities(user, subject)
13 14 15 16 17 18 19 20
      when Note then note_abilities(user, subject)
      when ProjectSnippet then project_snippet_abilities(user, subject)
      when PersonalSnippet then personal_snippet_abilities(user, subject)
      when MergeRequest then merge_request_abilities(user, subject)
      when Group then group_abilities(user, subject)
      when Namespace then namespace_abilities(user, subject)
      when GroupMember then group_member_abilities(user, subject)
      when ProjectMember then project_member_abilities(user, subject)
J
James Lopez 已提交
21
      else []
22 23 24
      end.concat(global_abilities(user))
    end

25 26
    # List of possible abilities for anonymous user
    def anonymous_abilities(user, subject)
D
Douwe Maan 已提交
27 28
      case true
      when subject.is_a?(PersonalSnippet)
29
        anonymous_personal_snippet_abilities(subject)
30
      when subject.is_a?(CommitStatus)
K
Kamil Trzcinski 已提交
31
        anonymous_commit_status_abilities(subject)
D
Douwe Maan 已提交
32
      when subject.is_a?(Project) || subject.respond_to?(:project)
33
        anonymous_project_abilities(subject)
D
Douwe Maan 已提交
34
      when subject.is_a?(Group) || subject.respond_to?(:group)
35
        anonymous_group_abilities(subject)
D
Douwe Maan 已提交
36 37 38
      else
        []
      end
39 40
    end

41
    def anonymous_project_abilities(subject)
D
Douwe Maan 已提交
42
      project = if subject.is_a?(Project)
43 44
                  subject
                else
45
                  subject.project
46 47
                end

48
      if project && project.public?
49
        rules = [
50 51 52
          :read_project,
          :read_wiki,
          :read_issue,
53
          :read_label,
54 55
          :read_milestone,
          :read_project_snippet,
56
          :read_project_member,
57 58
          :read_merge_request,
          :read_note,
59
          :read_commit_status,
60 61
          :download_code
        ]
62

K
Kamil Trzcinski 已提交
63
        # Allow to read builds by anonymous user if guests are allowed
64
        rules << :read_build if project.public_builds?
65

66
        rules - project_disabled_features_rules(project)
67
      else
68 69 70
        []
      end
    end
71

K
Kamil Trzcinski 已提交
72 73 74 75 76 77 78
    def anonymous_commit_status_abilities(subject)
      rules = anonymous_project_abilities(subject.project)
      # If subject is Ci::Build which inherits from CommitStatus filter the abilities
      rules = filter_build_abilities(rules) if subject.is_a?(Ci::Build)
      rules
    end

79
    def anonymous_group_abilities(subject)
D
Douwe Maan 已提交
80
      group = if subject.is_a?(Group)
81 82 83 84 85
                subject
              else
                subject.group
              end

86
      if group && group.projects.public_only.any?
87 88 89 90 91 92
        [:read_group]
      else
        []
      end
    end

93
    def anonymous_personal_snippet_abilities(snippet)
94 95 96 97
      if snippet.public?
        [:read_personal_snippet]
      else
        []
98 99 100
      end
    end

101 102 103 104
    def global_abilities(user)
      rules = []
      rules << :create_group if user.can_create_group
      rules
G
gitlabhq 已提交
105 106
    end

A
Andrey Kumanyaev 已提交
107 108
    def project_abilities(user, project)
      rules = []
S
skv-headless 已提交
109
      key = "/user/#{user.id}/project/#{project.id}"
110

S
skv-headless 已提交
111 112
      RequestStore.store[key] ||= begin
        team = project.team
G
gitlabhq 已提交
113

S
skv-headless 已提交
114 115
        # Rules based on role in project
        if team.master?(user)
116
          rules.push(*project_master_rules)
D
Dmitriy Zaporozhets 已提交
117

S
skv-headless 已提交
118
        elsif team.developer?(user)
119
          rules.push(*project_dev_rules)
120

S
skv-headless 已提交
121
        elsif team.reporter?(user)
122
          rules.push(*project_report_rules)
123

S
skv-headless 已提交
124
        elsif team.guest?(user)
125
          rules.push(*project_guest_rules)
S
skv-headless 已提交
126
        end
127

S
skv-headless 已提交
128
        if project.public? || project.internal?
129
          rules.push(*public_project_rules)
130

131
          # Allow to read builds for internal projects
132
          rules << :read_build if project.public_builds?
S
skv-headless 已提交
133
        end
134

S
skv-headless 已提交
135
        if project.owner == user || user.admin?
136
          rules.push(*project_admin_rules)
S
skv-headless 已提交
137
        end
138

S
skv-headless 已提交
139
        if project.group && project.group.has_owner?(user)
140
          rules.push(*project_admin_rules)
S
skv-headless 已提交
141
        end
142

S
skv-headless 已提交
143 144 145
        if project.archived?
          rules -= project_archived_rules
        end
146

147
        rules - project_disabled_features_rules(project)
148
      end
149 150
    end

151
    def public_project_rules
J
Jason Lee 已提交
152
      @public_project_rules ||= project_guest_rules + [
153
        :download_code,
154 155
        :fork_project,
        :read_commit_status,
156 157 158
      ]
    end

159
    def project_guest_rules
J
Jason Lee 已提交
160
      @project_guest_rules ||= [
A
Andrey Kumanyaev 已提交
161 162 163
        :read_project,
        :read_wiki,
        :read_issue,
164
        :read_label,
A
Andrey Kumanyaev 已提交
165
        :read_milestone,
A
Andrew8xx8 已提交
166
        :read_project_snippet,
167
        :read_project_member,
A
Andrey Kumanyaev 已提交
168 169
        :read_merge_request,
        :read_note,
170 171 172
        :create_project,
        :create_issue,
        :create_note
173 174
      ]
    end
D
Dmitriy Zaporozhets 已提交
175

176
    def project_report_rules
J
Jason Lee 已提交
177
      @project_report_rules ||= project_guest_rules + [
A
Andrey Kumanyaev 已提交
178
        :download_code,
179
        :fork_project,
180 181 182
        :create_project_snippet,
        :update_issue,
        :admin_issue,
183
        :admin_label,
184
        :read_commit_status,
185
        :read_build,
186 187
      ]
    end
D
Dmitriy Zaporozhets 已提交
188

189
    def project_dev_rules
J
Jason Lee 已提交
190
      @project_dev_rules ||= project_report_rules + [
191
        :admin_merge_request,
192
        :update_merge_request,
193 194 195 196
        :create_commit_status,
        :update_commit_status,
        :create_build,
        :update_build,
197 198
        :create_merge_request,
        :create_wiki,
199
        :push_code
200 201
      ]
    end
202

203
    def project_archived_rules
J
Jason Lee 已提交
204
      @project_archived_rules ||= [
205
        :create_merge_request,
206 207
        :push_code,
        :push_code_to_protected_branches,
208
        :update_merge_request,
209 210 211 212
        :admin_merge_request
      ]
    end

213
    def project_master_rules
J
Jason Lee 已提交
214
      @project_master_rules ||= project_dev_rules + [
215
        :push_code_to_protected_branches,
216
        :update_project_snippet,
A
Andrey Kumanyaev 已提交
217
        :admin_milestone,
A
Andrew8xx8 已提交
218
        :admin_project_snippet,
219
        :admin_project_member,
A
Andrey Kumanyaev 已提交
220 221
        :admin_merge_request,
        :admin_note,
222
        :admin_wiki,
223 224 225
        :admin_project,
        :admin_commit_status,
        :admin_build
226 227
      ]
    end
G
gitlabhq 已提交
228

229
    def project_admin_rules
J
Jason Lee 已提交
230
      @project_admin_rules ||= project_master_rules + [
231
        :change_namespace,
232
        :change_visibility_level,
233
        :rename_project,
234
        :remove_project,
235 236
        :archive_project,
        :remove_fork_project
237
      ]
A
Andrey Kumanyaev 已提交
238
    end
G
gitlabhq 已提交
239

240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
    def project_disabled_features_rules(project)
      rules = []

      unless project.issues_enabled
        rules += named_abilities('issue')
      end

      unless project.merge_requests_enabled
        rules += named_abilities('merge_request')
      end

      unless project.issues_enabled or project.merge_requests_enabled
        rules += named_abilities('label')
        rules += named_abilities('milestone')
      end

      unless project.snippets_enabled
        rules += named_abilities('project_snippet')
      end

      unless project.wiki_enabled
        rules += named_abilities('wiki')
      end

264 265 266 267
      unless project.builds_enabled
        rules += named_abilities('build')
      end

268 269 270
      rules
    end

271
    def group_abilities(user, group)
272 273
      rules = []

274
      if user.admin? || group.users.include?(user) || ProjectsFinder.new.execute(user, group: group).any?
275 276 277
        rules << :read_group
      end

F
Felipe Artur 已提交
278
      # Only group masters and group owners can create new projects and change permission level
279
      if group.has_master?(user) || group.has_owner?(user) || user.admin?
280
        rules += [
281
          :create_projects,
F
Felipe Artur 已提交
282 283
          :admin_milestones,
          :change_visibility_level
284
        ]
285 286
      end

287
      # Only group owner and administrators can admin group
288
      if group.has_owner?(user) || user.admin?
D
Douwe Maan 已提交
289 290 291 292 293
        rules += [
          :admin_group,
          :admin_namespace,
          :admin_group_member
        ]
294
      end
295 296 297 298

      rules.flatten
    end

299
    def namespace_abilities(user, namespace)
300 301
      rules = []

302
      # Only namespace owner and administrators can admin it
303
      if namespace.owner == user || user.admin?
D
Douwe Maan 已提交
304 305 306 307
        rules += [
          :create_projects,
          :admin_namespace
        ]
308 309 310 311 312
      end

      rules.flatten
    end

313
    [:issue, :merge_request].each do |name|
G
gitlabhq 已提交
314
      define_method "#{name}_abilities" do |user, subject|
315 316 317 318
        rules = []

        if subject.author == user || (subject.respond_to?(:assignee) && subject.assignee == user)
          rules += [
G
gitlabhq 已提交
319
            :"read_#{name}",
320
            :"update_#{name}",
G
gitlabhq 已提交
321
          ]
322 323 324 325 326 327 328
        end

        rules += project_abilities(user, subject.project)
        rules
      end
    end

329
    [:note, :project_snippet].each do |name|
330 331 332 333 334
      define_method "#{name}_abilities" do |user, subject|
        rules = []

        if subject.author == user
          rules += [
335
            :"read_#{name}",
336
            :"update_#{name}",
337
            :"admin_#{name}"
338
          ]
G
gitlabhq 已提交
339
        end
340 341 342 343 344 345

        if subject.respond_to?(:project) && subject.project
          rules += project_abilities(user, subject.project)
        end

        rules
G
gitlabhq 已提交
346 347
      end
    end
348

349 350 351 352 353 354 355 356 357 358 359 360
    def personal_snippet_abilities(user, snippet)
      rules = []

      if snippet.author == user
        rules += [
          :read_personal_snippet,
          :update_personal_snippet,
          :admin_personal_snippet
        ]
      end

      if snippet.public? || snippet.internal?
J
Jason Lee 已提交
361
        rules << :read_personal_snippet
362 363 364 365 366
      end

      rules
    end

367
    def group_member_abilities(user, subject)
368 369 370
      rules = []
      target_user = subject.user
      group = subject.group
371

D
Douwe Maan 已提交
372 373
      unless group.last_owner?(target_user)
        can_manage = group_abilities(user, group).include?(:admin_group_member)
374

375
        if can_manage
D
Douwe Maan 已提交
376 377
          rules << :update_group_member
          rules << :destroy_group_member
378
        elsif user == target_user
D
Douwe Maan 已提交
379 380
          rules << :destroy_group_member
        end
381
      end
382

383 384
      rules
    end
C
Ciro Santilli 已提交
385

386 387 388 389 390
    def project_member_abilities(user, subject)
      rules = []
      target_user = subject.user
      project = subject.project

D
Douwe Maan 已提交
391 392
      unless target_user == project.owner
        can_manage = project_abilities(user, project).include?(:admin_project_member)
393

394
        if can_manage
D
Douwe Maan 已提交
395 396
          rules << :update_project_member
          rules << :destroy_project_member
397
        elsif user == target_user
D
Douwe Maan 已提交
398 399
          rules << :destroy_project_member
        end
400
      end
D
Douwe Maan 已提交
401

402 403 404
      rules
    end

K
Kamil Trzcinski 已提交
405 406 407 408 409 410 411
    def commit_status_abilities(user, subject)
      rules = project_abilities(user, subject.project)
      # If subject is Ci::Build which inherits from CommitStatus filter the abilities
      rules = filter_build_abilities(rules) if subject.is_a?(Ci::Build)
      rules
    end

412 413 414
    def filter_build_abilities(rules)
      # If we can't read build we should also not have that
      # ability when looking at this in context of commit_status
415
      %w(read create update admin).each do |rule|
416
        rules.delete(:"#{rule}_commit_status") unless rules.include?(:"#{rule}_build")
417 418 419 420
      end
      rules
    end

C
Ciro Santilli 已提交
421 422
    def abilities
      @abilities ||= begin
423 424 425 426
        abilities = Six.new
        abilities << self
        abilities
      end
C
Ciro Santilli 已提交
427
    end
428

429 430 431 432
    def external_issue_abilities(user, subject)
      project_abilities(user, subject.project)
    end

433 434 435 436 437
    private

    def named_abilities(name)
      [
        :"read_#{name}",
438 439
        :"create_#{name}",
        :"update_#{name}",
440 441 442
        :"admin_#{name}"
      ]
    end
G
gitlabhq 已提交
443
  end
G
gitlabhq 已提交
444
end