ability.rb 13.6 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

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

46
    def anonymous_project_abilities(subject)
D
Douwe Maan 已提交
47
      project = if subject.is_a?(Project)
48 49
                  subject
                else
50
                  subject.project
51 52
                end

53
      if project && project.public?
54
        rules = [
55 56
          :read_project,
          :read_wiki,
57
          :read_label,
58 59
          :read_milestone,
          :read_project_snippet,
60
          :read_project_member,
61 62
          :read_merge_request,
          :read_note,
63
          :read_commit_status,
64 65
          :download_code
        ]
66

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

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

F
Felipe Artur 已提交
73 74 75 76
        # Allow anonymous users to read project members if public is not a restricted level
        restricted_public_level = current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC)
        rules << :read_project_member unless restricted_public_level

77
        rules - project_disabled_features_rules(project)
78
      else
79 80 81
        []
      end
    end
82

K
Kamil Trzcinski 已提交
83 84 85 86 87 88 89
    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

90
    def anonymous_group_abilities(subject)
F
Felipe Artur 已提交
91 92
      rules = []

D
Douwe Maan 已提交
93
      group = if subject.is_a?(Group)
94 95 96 97 98
                subject
              else
                subject.group
              end

F
Felipe Artur 已提交
99 100 101 102 103 104
      if group
        rules << [:read_group] if group.public?

        # Allow anonymous users to read project members if public is not a restricted level
        restricted_public_level = current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC)
        rules << [:read_group_members] unless restricted_public_level
105
      end
F
Felipe Artur 已提交
106 107

      rules
108 109
    end

110
    def anonymous_personal_snippet_abilities(snippet)
111 112 113 114
      if snippet.public?
        [:read_personal_snippet]
      else
        []
115 116 117
      end
    end

118 119 120 121 122 123 124 125
    def anonymous_project_snippet_abilities(snippet)
      if snippet.public?
        [:read_project_snippet]
      else
        []
      end
    end

F
Felipe Artur 已提交
126 127 128 129 130
    def anonymous_user_abilities()
      restricted_by_public = current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC)
      [:read_user] unless restricted_by_public
    end

131 132 133 134
    def global_abilities(user)
      rules = []
      rules << :create_group if user.can_create_group
      rules
G
gitlabhq 已提交
135 136
    end

A
Andrey Kumanyaev 已提交
137 138
    def project_abilities(user, project)
      rules = []
S
skv-headless 已提交
139
      key = "/user/#{user.id}/project/#{project.id}"
140

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

D
Douwe Maan 已提交
145 146 147 148 149 150 151
        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 已提交
152
        if project.public? || (project.internal? && !user.external?)
153
          rules.push(*public_project_rules)
154

155
          # Allow to read builds for internal projects
156
          rules << :read_build if project.public_builds?
S
skv-headless 已提交
157
        end
158

S
skv-headless 已提交
159 160 161
        if project.archived?
          rules -= project_archived_rules
        end
162

163
        rules - project_disabled_features_rules(project)
164
      end
165 166
    end

Z
Zeger-Jan van de Weg 已提交
167 168 169 170 171 172 173 174 175 176 177 178 179
    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

180
    def public_project_rules
J
Jason Lee 已提交
181
      @public_project_rules ||= project_guest_rules + [
182
        :download_code,
183 184
        :fork_project,
        :read_commit_status,
F
Felipe Artur 已提交
185
        :read_project_members
186 187 188
      ]
    end

189
    def project_guest_rules
J
Jason Lee 已提交
190
      @project_guest_rules ||= [
A
Andrey Kumanyaev 已提交
191 192 193
        :read_project,
        :read_wiki,
        :read_issue,
194
        :read_label,
A
Andrey Kumanyaev 已提交
195
        :read_milestone,
A
Andrew8xx8 已提交
196
        :read_project_snippet,
197
        :read_project_member,
A
Andrey Kumanyaev 已提交
198 199
        :read_merge_request,
        :read_note,
200 201
        :create_project,
        :create_issue,
D
Douwe Maan 已提交
202 203
        :create_note,
        :upload_file
204 205
      ]
    end
D
Dmitriy Zaporozhets 已提交
206

207
    def project_report_rules
J
Jason Lee 已提交
208
      @project_report_rules ||= project_guest_rules + [
A
Andrey Kumanyaev 已提交
209
        :download_code,
210
        :fork_project,
211 212 213
        :create_project_snippet,
        :update_issue,
        :admin_issue,
214
        :admin_label,
215
        :read_commit_status,
216
        :read_build,
217 218
      ]
    end
D
Dmitriy Zaporozhets 已提交
219

220
    def project_dev_rules
J
Jason Lee 已提交
221
      @project_dev_rules ||= project_report_rules + [
222
        :admin_merge_request,
223
        :update_merge_request,
224 225 226 227
        :create_commit_status,
        :update_commit_status,
        :create_build,
        :update_build,
228 229
        :create_merge_request,
        :create_wiki,
230
        :push_code
231 232
      ]
    end
233

234
    def project_archived_rules
J
Jason Lee 已提交
235
      @project_archived_rules ||= [
236
        :create_merge_request,
237 238
        :push_code,
        :push_code_to_protected_branches,
239
        :update_merge_request,
240 241 242 243
        :admin_merge_request
      ]
    end

244
    def project_master_rules
J
Jason Lee 已提交
245
      @project_master_rules ||= project_dev_rules + [
246
        :push_code_to_protected_branches,
247
        :update_project_snippet,
A
Andrey Kumanyaev 已提交
248
        :admin_milestone,
A
Andrew8xx8 已提交
249
        :admin_project_snippet,
250
        :admin_project_member,
A
Andrey Kumanyaev 已提交
251 252
        :admin_merge_request,
        :admin_note,
253
        :admin_wiki,
254 255 256
        :admin_project,
        :admin_commit_status,
        :admin_build
257 258
      ]
    end
G
gitlabhq 已提交
259

D
Douwe Maan 已提交
260 261
    def project_owner_rules
      @project_owner_rules ||= project_master_rules + [
262
        :change_namespace,
263
        :change_visibility_level,
264
        :rename_project,
265
        :remove_project,
266
        :archive_project,
267
        :remove_fork_project,
268 269
        :destroy_merge_request,
        :destroy_issue
270
      ]
A
Andrey Kumanyaev 已提交
271
    end
G
gitlabhq 已提交
272

273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
    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

297 298 299 300
      unless project.builds_enabled
        rules += named_abilities('build')
      end

301 302 303
      rules
    end

304
    def group_abilities(user, group)
305 306
      rules = []

F
Felipe Artur 已提交
307
      rules << [:read_group, :read_group_members] if can_read_group?(user, group)
308

D
Douwe Maan 已提交
309
      # Only group masters and group owners can create new projects
310
      if group.has_master?(user) || group.has_owner?(user) || user.admin?
311
        rules += [
312
          :create_projects,
F
Felipe Artur 已提交
313
          :admin_milestones
314
        ]
315 316
      end

317
      # Only group owner and administrators can admin group
318
      if group.has_owner?(user) || user.admin?
D
Douwe Maan 已提交
319 320 321
        rules += [
          :admin_group,
          :admin_namespace,
F
Felipe Artur 已提交
322 323
          :admin_group_member,
          :change_visibility_level
D
Douwe Maan 已提交
324
        ]
325
      end
326 327 328 329

      rules.flatten
    end

F
Felipe Artur 已提交
330
    def can_read_group?(user, group)
D
Douwe Maan 已提交
331 332 333 334 335 336
      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 已提交
337 338
    end

339
    def namespace_abilities(user, namespace)
340 341
      rules = []

342
      # Only namespace owner and administrators can admin it
343
      if namespace.owner == user || user.admin?
D
Douwe Maan 已提交
344 345 346 347
        rules += [
          :create_projects,
          :admin_namespace
        ]
348 349 350 351 352
      end

      rules.flatten
    end

353
    [:issue, :merge_request].each do |name|
G
gitlabhq 已提交
354
      define_method "#{name}_abilities" do |user, subject|
355 356 357 358
        rules = []

        if subject.author == user || (subject.respond_to?(:assignee) && subject.assignee == user)
          rules += [
G
gitlabhq 已提交
359
            :"read_#{name}",
360
            :"update_#{name}",
G
gitlabhq 已提交
361
          ]
362 363 364
        end

        rules += project_abilities(user, subject.project)
365
        rules = filter_confidential_issues_abilities(user, subject, rules) if subject.is_a?(Issue)
366 367 368 369
        rules
      end
    end

370 371
    def note_abilities(user, note)
      rules = []
372

373 374 375 376 377 378 379
      if note.author == user
        rules += [
          :read_note,
          :update_note,
          :admin_note
        ]
      end
380

381 382
      if note.respond_to?(:project) && note.project
        rules += project_abilities(user, note.project)
G
gitlabhq 已提交
383
      end
384 385

      rules
G
gitlabhq 已提交
386
    end
387

388 389 390 391 392 393 394 395 396 397 398
    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 已提交
399
      if snippet.public? || (snippet.internal? && !user.external?)
J
Jason Lee 已提交
400
        rules << :read_personal_snippet
401 402 403 404 405
      end

      rules
    end

406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
    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

424
    def group_member_abilities(user, subject)
425 426 427
      rules = []
      target_user = subject.user
      group = subject.group
428

D
Douwe Maan 已提交
429 430
      unless group.last_owner?(target_user)
        can_manage = group_abilities(user, group).include?(:admin_group_member)
431

432
        if can_manage
D
Douwe Maan 已提交
433 434
          rules << :update_group_member
          rules << :destroy_group_member
435
        elsif user == target_user
D
Douwe Maan 已提交
436 437
          rules << :destroy_group_member
        end
438
      end
439

440 441
      rules
    end
C
Ciro Santilli 已提交
442

443 444 445 446 447
    def project_member_abilities(user, subject)
      rules = []
      target_user = subject.user
      project = subject.project

D
Douwe Maan 已提交
448 449
      unless target_user == project.owner
        can_manage = project_abilities(user, project).include?(:admin_project_member)
450

451
        if can_manage
D
Douwe Maan 已提交
452 453
          rules << :update_project_member
          rules << :destroy_project_member
454
        elsif user == target_user
D
Douwe Maan 已提交
455 456
          rules << :destroy_project_member
        end
457
      end
D
Douwe Maan 已提交
458

459 460 461
      rules
    end

K
Kamil Trzcinski 已提交
462 463 464 465 466 467 468
    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

469 470 471
    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
472
      %w(read create update admin).each do |rule|
473
        rules.delete(:"#{rule}_commit_status") unless rules.include?(:"#{rule}_build")
474 475 476 477
      end
      rules
    end

F
Felipe Artur 已提交
478 479 480 481
    def user_abilities()
      [:read_user]
    end

C
Ciro Santilli 已提交
482 483
    def abilities
      @abilities ||= begin
484 485 486 487
        abilities = Six.new
        abilities << self
        abilities
      end
C
Ciro Santilli 已提交
488
    end
489

490 491 492 493
    def external_issue_abilities(user, subject)
      project_abilities(user, subject.project)
    end

494 495 496 497 498
    private

    def named_abilities(name)
      [
        :"read_#{name}",
499 500
        :"create_#{name}",
        :"update_#{name}",
501 502 503
        :"admin_#{name}"
      ]
    end
504 505 506 507 508 509 510 511 512 513 514 515

    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 已提交
516
  end
G
gitlabhq 已提交
517
end