ability.rb 14.0 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)
F
Felipe Artur 已提交
21
      when User then user_abilities
J
James Lopez 已提交
22
      else []
23 24 25
      end.concat(global_abilities(user))
    end

Y
Yorick Peterse 已提交
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
    # Given a list of users and a project this method returns the users that can
    # read the given project.
    def users_that_can_read_project(users, project)
      if project.public?
        users
      else
        users.select do |user|
          if user.admin?
            true
          elsif project.internal? && !user.external?
            true
          elsif project.owner == user
            true
          elsif project.team.members.include?(user)
            true
          else
            false
          end
        end
      end
    end

48 49
    # List of possible abilities for anonymous user
    def anonymous_abilities(user, subject)
50
      if subject.is_a?(PersonalSnippet)
51
        anonymous_personal_snippet_abilities(subject)
52
      elsif subject.is_a?(ProjectSnippet)
53
        anonymous_project_snippet_abilities(subject)
54
      elsif subject.is_a?(CommitStatus)
K
Kamil Trzcinski 已提交
55
        anonymous_commit_status_abilities(subject)
56
      elsif subject.is_a?(Project) || subject.respond_to?(:project)
57
        anonymous_project_abilities(subject)
58
      elsif subject.is_a?(Group) || subject.respond_to?(:group)
59
        anonymous_group_abilities(subject)
60
      elsif subject.is_a?(User)
F
Felipe Artur 已提交
61
        anonymous_user_abilities
D
Douwe Maan 已提交
62 63 64
      else
        []
      end
65 66
    end

67
    def anonymous_project_abilities(subject)
D
Douwe Maan 已提交
68
      project = if subject.is_a?(Project)
69 70
                  subject
                else
71
                  subject.project
72 73
                end

74
      if project && project.public?
75
        rules = [
76 77
          :read_project,
          :read_wiki,
78
          :read_label,
79 80
          :read_milestone,
          :read_project_snippet,
81
          :read_project_member,
82 83
          :read_merge_request,
          :read_note,
84
          :read_pipeline,
85
          :read_commit_status,
K
Kamil Trzcinski 已提交
86
          :read_container_image,
87 88
          :download_code
        ]
89

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

93 94 95
        # Allow to read issues by anonymous user if issue is not confidential
        rules << :read_issue unless subject.is_a?(Issue) && subject.confidential?

96
        rules - project_disabled_features_rules(project)
97
      else
98 99 100
        []
      end
    end
101

K
Kamil Trzcinski 已提交
102 103 104 105 106 107 108
    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

109
    def anonymous_group_abilities(subject)
F
Felipe Artur 已提交
110 111
      rules = []

D
Douwe Maan 已提交
112
      group = if subject.is_a?(Group)
113 114 115 116 117
                subject
              else
                subject.group
              end

F
Felipe Artur 已提交
118
      rules << :read_group if group.public?
F
Felipe Artur 已提交
119 120

      rules
121 122
    end

123
    def anonymous_personal_snippet_abilities(snippet)
124 125 126 127
      if snippet.public?
        [:read_personal_snippet]
      else
        []
128 129 130
      end
    end

131 132 133 134 135 136 137 138
    def anonymous_project_snippet_abilities(snippet)
      if snippet.public?
        [:read_project_snippet]
      else
        []
      end
    end

F
Felipe Artur 已提交
139 140
    def anonymous_user_abilities
      [:read_user] unless restricted_public_level?
F
Felipe Artur 已提交
141 142
    end

143 144 145
    def global_abilities(user)
      rules = []
      rules << :create_group if user.can_create_group
146
      rules << :read_users_list
147
      rules
G
gitlabhq 已提交
148 149
    end

A
Andrey Kumanyaev 已提交
150 151
    def project_abilities(user, project)
      rules = []
S
skv-headless 已提交
152
      key = "/user/#{user.id}/project/#{project.id}"
153

S
skv-headless 已提交
154
      RequestStore.store[key] ||= begin
Z
Zeger-Jan van de Weg 已提交
155 156
        # Push abilities on the users team role
        rules.push(*project_team_rules(project.team, user))
G
gitlabhq 已提交
157

D
Douwe Maan 已提交
158 159 160 161 162 163 164
        if project.owner == user ||
          (project.group && project.group.has_owner?(user)) ||
          user.admin?

          rules.push(*project_owner_rules)
        end

Z
Zeger-Jan van de Weg 已提交
165
        if project.public? || (project.internal? && !user.external?)
166
          rules.push(*public_project_rules)
167

168
          # Allow to read builds for internal projects
169
          rules << :read_build if project.public_builds?
S
skv-headless 已提交
170
        end
171

S
skv-headless 已提交
172 173 174
        if project.archived?
          rules -= project_archived_rules
        end
175

176
        rules - project_disabled_features_rules(project)
177
      end
178 179
    end

Z
Zeger-Jan van de Weg 已提交
180 181 182 183 184 185 186 187 188 189 190 191 192
    def project_team_rules(team, user)
      # Rules based on role in project
      if team.master?(user)
        project_master_rules
      elsif team.developer?(user)
        project_dev_rules
      elsif team.reporter?(user)
        project_report_rules
      elsif team.guest?(user)
        project_guest_rules
      end
    end

193
    def public_project_rules
J
Jason Lee 已提交
194
      @public_project_rules ||= project_guest_rules + [
195
        :download_code,
196
        :fork_project,
F
Felipe Artur 已提交
197
        :read_commit_status
198 199 200
      ]
    end

201
    def project_guest_rules
J
Jason Lee 已提交
202
      @project_guest_rules ||= [
A
Andrey Kumanyaev 已提交
203 204 205
        :read_project,
        :read_wiki,
        :read_issue,
206
        :read_label,
A
Andrey Kumanyaev 已提交
207
        :read_milestone,
A
Andrew8xx8 已提交
208
        :read_project_snippet,
209
        :read_project_member,
A
Andrey Kumanyaev 已提交
210 211
        :read_merge_request,
        :read_note,
212 213
        :create_project,
        :create_issue,
D
Douwe Maan 已提交
214 215
        :create_note,
        :upload_file
216 217
      ]
    end
D
Dmitriy Zaporozhets 已提交
218

219
    def project_report_rules
J
Jason Lee 已提交
220
      @project_report_rules ||= project_guest_rules + [
A
Andrey Kumanyaev 已提交
221
        :download_code,
222
        :fork_project,
223 224 225
        :create_project_snippet,
        :update_issue,
        :admin_issue,
226
        :admin_label,
227
        :read_commit_status,
228
        :read_build,
K
Kamil Trzcinski 已提交
229
        :read_container_image,
K
WIP  
Kamil Trzcinski 已提交
230
        :read_pipeline,
231 232
      ]
    end
D
Dmitriy Zaporozhets 已提交
233

234
    def project_dev_rules
J
Jason Lee 已提交
235
      @project_dev_rules ||= project_report_rules + [
236
        :admin_merge_request,
237
        :update_merge_request,
238 239 240 241
        :create_commit_status,
        :update_commit_status,
        :create_build,
        :update_build,
K
WIP  
Kamil Trzcinski 已提交
242 243
        :create_pipeline,
        :update_pipeline,
244 245
        :create_merge_request,
        :create_wiki,
246
        :push_code,
K
Kamil Trzcinski 已提交
247 248
        :create_container_image,
        :update_container_image,
249 250
      ]
    end
251

252
    def project_archived_rules
J
Jason Lee 已提交
253
      @project_archived_rules ||= [
254
        :create_merge_request,
255 256
        :push_code,
        :push_code_to_protected_branches,
257
        :update_merge_request,
258 259 260 261
        :admin_merge_request
      ]
    end

262
    def project_master_rules
J
Jason Lee 已提交
263
      @project_master_rules ||= project_dev_rules + [
264
        :push_code_to_protected_branches,
265
        :update_project_snippet,
A
Andrey Kumanyaev 已提交
266
        :admin_milestone,
A
Andrew8xx8 已提交
267
        :admin_project_snippet,
268
        :admin_project_member,
A
Andrey Kumanyaev 已提交
269 270
        :admin_merge_request,
        :admin_note,
271
        :admin_wiki,
272 273
        :admin_project,
        :admin_commit_status,
K
WIP  
Kamil Trzcinski 已提交
274
        :admin_build,
K
Kamil Trzcinski 已提交
275
        :admin_container_image,
K
WIP  
Kamil Trzcinski 已提交
276
        :admin_pipeline
277 278
      ]
    end
G
gitlabhq 已提交
279

D
Douwe Maan 已提交
280 281
    def project_owner_rules
      @project_owner_rules ||= project_master_rules + [
282
        :change_namespace,
283
        :change_visibility_level,
284
        :rename_project,
285
        :remove_project,
286
        :archive_project,
287
        :remove_fork_project,
288 289
        :destroy_merge_request,
        :destroy_issue
290
      ]
A
Andrey Kumanyaev 已提交
291
    end
G
gitlabhq 已提交
292

293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
    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

317 318
      unless project.builds_enabled
        rules += named_abilities('build')
K
WIP  
Kamil Trzcinski 已提交
319
        rules += named_abilities('pipeline')
320 321
      end

322
      unless project.container_registry_enabled
K
Kamil Trzcinski 已提交
323
        rules += named_abilities('container_image')
324 325
      end

326 327 328
      rules
    end

329
    def group_abilities(user, group)
330
      rules = []
F
Felipe Artur 已提交
331
      rules << :read_group if can_read_group?(user, group)
332

D
Douwe Maan 已提交
333
      # Only group masters and group owners can create new projects
334
      if group.has_master?(user) || group.has_owner?(user) || user.admin?
335
        rules += [
336
          :create_projects,
F
Felipe Artur 已提交
337
          :admin_milestones
338
        ]
339 340
      end

341
      # Only group owner and administrators can admin group
342
      if group.has_owner?(user) || user.admin?
D
Douwe Maan 已提交
343 344 345
        rules += [
          :admin_group,
          :admin_namespace,
F
Felipe Artur 已提交
346 347
          :admin_group_member,
          :change_visibility_level
D
Douwe Maan 已提交
348
        ]
349
      end
350 351 352 353

      rules.flatten
    end

F
Felipe Artur 已提交
354
    def can_read_group?(user, group)
D
Douwe Maan 已提交
355 356 357 358 359 360
      return true if user.admin?
      return true if group.public?
      return true if group.internal? && !user.external?
      return true if group.users.include?(user)

      GroupProjectsFinder.new(group).execute(user).any?
F
Felipe Artur 已提交
361 362
    end

363
    def namespace_abilities(user, namespace)
364 365
      rules = []

366
      # Only namespace owner and administrators can admin it
367
      if namespace.owner == user || user.admin?
D
Douwe Maan 已提交
368 369 370 371
        rules += [
          :create_projects,
          :admin_namespace
        ]
372 373 374 375 376
      end

      rules.flatten
    end

377
    [:issue, :merge_request].each do |name|
G
gitlabhq 已提交
378
      define_method "#{name}_abilities" do |user, subject|
379 380 381 382
        rules = []

        if subject.author == user || (subject.respond_to?(:assignee) && subject.assignee == user)
          rules += [
G
gitlabhq 已提交
383
            :"read_#{name}",
384
            :"update_#{name}",
G
gitlabhq 已提交
385
          ]
386 387 388
        end

        rules += project_abilities(user, subject.project)
389
        rules = filter_confidential_issues_abilities(user, subject, rules) if subject.is_a?(Issue)
390 391 392 393
        rules
      end
    end

394 395
    def note_abilities(user, note)
      rules = []
396

397 398 399 400 401 402 403
      if note.author == user
        rules += [
          :read_note,
          :update_note,
          :admin_note
        ]
      end
404

405 406
      if note.respond_to?(:project) && note.project
        rules += project_abilities(user, note.project)
G
gitlabhq 已提交
407
      end
408 409

      rules
G
gitlabhq 已提交
410
    end
411

412 413 414 415 416 417 418 419 420 421 422
    def personal_snippet_abilities(user, snippet)
      rules = []

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

Z
Zeger-Jan van de Weg 已提交
423
      if snippet.public? || (snippet.internal? && !user.external?)
J
Jason Lee 已提交
424
        rules << :read_personal_snippet
425 426 427 428 429
      end

      rules
    end

430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
    def project_snippet_abilities(user, snippet)
      rules = []

      if snippet.author == user || user.admin?
        rules += [
          :read_project_snippet,
          :update_project_snippet,
          :admin_project_snippet
        ]
      end

      if snippet.public? || (snippet.internal? && !user.external?) || (snippet.private? && snippet.project.team.member?(user))
        rules << :read_project_snippet
      end

      rules
    end

448
    def group_member_abilities(user, subject)
449 450 451
      rules = []
      target_user = subject.user
      group = subject.group
452

D
Douwe Maan 已提交
453 454
      unless group.last_owner?(target_user)
        can_manage = group_abilities(user, group).include?(:admin_group_member)
455

456
        if can_manage
D
Douwe Maan 已提交
457 458
          rules << :update_group_member
          rules << :destroy_group_member
459
        elsif user == target_user
D
Douwe Maan 已提交
460 461
          rules << :destroy_group_member
        end
462
      end
463

464 465
      rules
    end
C
Ciro Santilli 已提交
466

467 468 469 470 471
    def project_member_abilities(user, subject)
      rules = []
      target_user = subject.user
      project = subject.project

D
Douwe Maan 已提交
472 473
      unless target_user == project.owner
        can_manage = project_abilities(user, project).include?(:admin_project_member)
474

475
        if can_manage
D
Douwe Maan 已提交
476 477
          rules << :update_project_member
          rules << :destroy_project_member
478
        elsif user == target_user
D
Douwe Maan 已提交
479 480
          rules << :destroy_project_member
        end
481
      end
D
Douwe Maan 已提交
482

483 484 485
      rules
    end

K
Kamil Trzcinski 已提交
486 487 488 489 490 491 492
    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

493 494 495
    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
496
      %w(read create update admin).each do |rule|
497
        rules.delete(:"#{rule}_commit_status") unless rules.include?(:"#{rule}_build")
498 499 500 501
      end
      rules
    end

F
Felipe Artur 已提交
502
    def user_abilities
F
Felipe Artur 已提交
503 504 505
      [:read_user]
    end

C
Ciro Santilli 已提交
506 507
    def abilities
      @abilities ||= begin
508 509 510 511
        abilities = Six.new
        abilities << self
        abilities
      end
C
Ciro Santilli 已提交
512
    end
513

514 515 516 517
    def external_issue_abilities(user, subject)
      project_abilities(user, subject.project)
    end

518 519
    private

F
Felipe Artur 已提交
520
    def restricted_public_level?
F
Felipe Artur 已提交
521
      current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC)
F
Felipe Artur 已提交
522 523
    end

524 525 526
    def named_abilities(name)
      [
        :"read_#{name}",
527 528
        :"create_#{name}",
        :"update_#{name}",
529 530 531
        :"admin_#{name}"
      ]
    end
532 533 534 535 536 537 538 539 540 541 542 543

    def filter_confidential_issues_abilities(user, issue, rules)
      return rules if user.admin? || !issue.confidential?

      unless issue.author == user || issue.assignee == user || issue.project.team.member?(user.id)
        rules.delete(:admin_issue)
        rules.delete(:read_issue)
        rules.delete(:update_issue)
      end

      rules
    end
G
gitlabhq 已提交
544
  end
G
gitlabhq 已提交
545
end