BigW Consortium Gitlab

ability.rb 11.8 KB
Newer Older
gitlabhq committed
1
class Ability
Andrey Kumanyaev committed
2
  class << self
3
    def allowed(user, subject)
4
      return anonymous_abilities(user, subject) if user.nil?
Douwe Maan committed
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)
James Lopez committed
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)
Douwe Maan committed
27 28
      case true
      when subject.is_a?(PersonalSnippet)
29
        anonymous_personal_snippet_abilities(subject)
30
      when subject.is_a?(CommitStatus)
Kamil Trzcinski committed
31
        anonymous_commit_status_abilities(subject)
Douwe Maan committed
32
      when subject.is_a?(Project) || subject.respond_to?(:project)
33
        anonymous_project_abilities(subject)
Douwe Maan committed
34
      when subject.is_a?(Group) || subject.respond_to?(:group)
35
        anonymous_group_abilities(subject)
Douwe Maan committed
36 37 38
      else
        []
      end
39 40
    end

41
    def anonymous_project_abilities(subject)
Douwe Maan committed
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
          :read_project,
          :read_wiki,
52
          :read_label,
53 54
          :read_milestone,
          :read_project_snippet,
55
          :read_project_member,
56 57
          :read_merge_request,
          :read_note,
58
          :read_commit_status,
59 60
          :download_code
        ]
61

Kamil Trzcinski committed
62
        # Allow to read builds by anonymous user if guests are allowed
63
        rules << :read_build if project.public_builds?
64

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

68
        rules - project_disabled_features_rules(project)
69
      else
70 71 72
        []
      end
    end
73

Kamil Trzcinski committed
74 75 76 77 78 79 80
    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

81
    def anonymous_group_abilities(subject)
Douwe Maan committed
82
      group = if subject.is_a?(Group)
83 84 85 86 87
                subject
              else
                subject.group
              end

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

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

103 104 105 106
    def global_abilities(user)
      rules = []
      rules << :create_group if user.can_create_group
      rules
gitlabhq committed
107 108
    end

Andrey Kumanyaev committed
109 110
    def project_abilities(user, project)
      rules = []
111
      key = "/user/#{user.id}/project/#{project.id}"
112

113
      RequestStore.store[key] ||= begin
Zeger-Jan van de Weg committed
114 115
        # Push abilities on the users team role
        rules.push(*project_team_rules(project.team, user))
gitlabhq committed
116

Zeger-Jan van de Weg committed
117
        if project.public? || (project.internal? && !user.external?)
118
          rules.push(*public_project_rules)
119

120
          # Allow to read builds for internal projects
121
          rules << :read_build if project.public_builds?
122
        end
123

124
        if project.owner == user || user.admin?
125
          rules.push(*project_admin_rules)
126
        end
127

128
        if project.group && project.group.has_owner?(user)
129
          rules.push(*project_admin_rules)
130
        end
131

132 133 134
        if project.archived?
          rules -= project_archived_rules
        end
135

136
        rules - project_disabled_features_rules(project)
137
      end
138 139
    end

Zeger-Jan van de Weg committed
140 141 142 143 144 145 146 147 148 149 150 151 152
    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

153
    def public_project_rules
154
      @public_project_rules ||= project_guest_rules + [
155
        :download_code,
156 157
        :fork_project,
        :read_commit_status,
158 159 160
      ]
    end

161
    def project_guest_rules
162
      @project_guest_rules ||= [
Andrey Kumanyaev committed
163 164 165
        :read_project,
        :read_wiki,
        :read_issue,
166
        :read_label,
Andrey Kumanyaev committed
167
        :read_milestone,
Andrew8xx8 committed
168
        :read_project_snippet,
169
        :read_project_member,
Andrey Kumanyaev committed
170 171
        :read_merge_request,
        :read_note,
172 173 174
        :create_project,
        :create_issue,
        :create_note
175 176
      ]
    end
Dmitriy Zaporozhets committed
177

178
    def project_report_rules
179
      @project_report_rules ||= project_guest_rules + [
Andrey Kumanyaev committed
180
        :download_code,
181
        :fork_project,
182 183 184
        :create_project_snippet,
        :update_issue,
        :admin_issue,
185
        :admin_label,
186
        :read_commit_status,
187
        :read_build,
188 189
      ]
    end
Dmitriy Zaporozhets committed
190

191
    def project_dev_rules
192
      @project_dev_rules ||= project_report_rules + [
193
        :admin_merge_request,
194
        :update_merge_request,
195 196 197 198
        :create_commit_status,
        :update_commit_status,
        :create_build,
        :update_build,
199 200
        :create_merge_request,
        :create_wiki,
201
        :push_code
202 203
      ]
    end
204

205
    def project_archived_rules
206
      @project_archived_rules ||= [
207
        :create_merge_request,
208 209
        :push_code,
        :push_code_to_protected_branches,
210
        :update_merge_request,
211 212 213 214
        :admin_merge_request
      ]
    end

215
    def project_master_rules
216
      @project_master_rules ||= project_dev_rules + [
217
        :push_code_to_protected_branches,
218
        :update_project_snippet,
Andrey Kumanyaev committed
219
        :admin_milestone,
Andrew8xx8 committed
220
        :admin_project_snippet,
221
        :admin_project_member,
Andrey Kumanyaev committed
222 223
        :admin_merge_request,
        :admin_note,
224
        :admin_wiki,
225 226 227
        :admin_project,
        :admin_commit_status,
        :admin_build
228 229
      ]
    end
gitlabhq committed
230

231
    def project_admin_rules
232
      @project_admin_rules ||= project_master_rules + [
233
        :change_namespace,
234
        :change_visibility_level,
235
        :rename_project,
236
        :remove_project,
237 238
        :archive_project,
        :remove_fork_project
239
      ]
Andrey Kumanyaev committed
240
    end
gitlabhq committed
241

242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
    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

266 267 268 269
      unless project.builds_enabled
        rules += named_abilities('build')
      end

270 271 272
      rules
    end

273
    def group_abilities(user, group)
274 275
      rules = []

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

280 281
      # Only group masters and group owners can create new projects in group
      if group.has_master?(user) || group.has_owner?(user) || user.admin?
282
        rules += [
283
          :create_projects,
284
          :admin_milestones
285
        ]
286 287
      end

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

      rules.flatten
    end

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

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

      rules.flatten
    end

314
    [:issue, :merge_request].each do |name|
gitlabhq committed
315
      define_method "#{name}_abilities" do |user, subject|
316 317 318 319
        rules = []

        if subject.author == user || (subject.respond_to?(:assignee) && subject.assignee == user)
          rules += [
gitlabhq committed
320
            :"read_#{name}",
321
            :"update_#{name}",
gitlabhq committed
322
          ]
323 324 325
        end

        rules += project_abilities(user, subject.project)
326
        rules = filter_confidential_issues_abilities(user, subject, rules) if subject.is_a?(Issue)
327 328 329 330
        rules
      end
    end

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

        if subject.author == user
          rules += [
337
            :"read_#{name}",
338
            :"update_#{name}",
339
            :"admin_#{name}"
340
          ]
gitlabhq committed
341
        end
342 343 344 345 346 347

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

        rules
gitlabhq committed
348 349
      end
    end
350

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

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

Zeger-Jan van de Weg committed
362
      if snippet.public? || (snippet.internal? && !user.external?)
363
        rules << :read_personal_snippet
364 365 366 367 368
      end

      rules
    end

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

Douwe Maan committed
374 375
      unless group.last_owner?(target_user)
        can_manage = group_abilities(user, group).include?(:admin_group_member)
376

377
        if can_manage
Douwe Maan committed
378 379
          rules << :update_group_member
          rules << :destroy_group_member
380
        elsif user == target_user
Douwe Maan committed
381 382
          rules << :destroy_group_member
        end
383
      end
384

385 386
      rules
    end
Ciro Santilli committed
387

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

Douwe Maan committed
393 394
      unless target_user == project.owner
        can_manage = project_abilities(user, project).include?(:admin_project_member)
395

396
        if can_manage
Douwe Maan committed
397 398
          rules << :update_project_member
          rules << :destroy_project_member
399
        elsif user == target_user
Douwe Maan committed
400 401
          rules << :destroy_project_member
        end
402
      end
Douwe Maan committed
403

404 405 406
      rules
    end

Kamil Trzcinski committed
407 408 409 410 411 412 413
    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

414 415 416
    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
417
      %w(read create update admin).each do |rule|
418
        rules.delete(:"#{rule}_commit_status") unless rules.include?(:"#{rule}_build")
419 420 421 422
      end
      rules
    end

Ciro Santilli committed
423 424
    def abilities
      @abilities ||= begin
425 426 427 428
        abilities = Six.new
        abilities << self
        abilities
      end
Ciro Santilli committed
429
    end
430

431 432 433 434
    def external_issue_abilities(user, subject)
      project_abilities(user, subject.project)
    end

435 436 437 438 439
    private

    def named_abilities(name)
      [
        :"read_#{name}",
440 441
        :"create_#{name}",
        :"update_#{name}",
442 443 444
        :"admin_#{name}"
      ]
    end
445 446 447 448 449 450 451 452 453 454 455 456

    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
gitlabhq committed
457
  end
gitlabhq committed
458
end