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

K
Kamil Trzcinski 已提交
8
      # We check with `is_a?`, because CommitStatus uses inheritance
9
      if subject.is_a?(CommitStatus)
K
Kamil Trzcinski 已提交
10
        return commit_status_abilities(user, subject)
11 12
      end

A
Andrey Kumanyaev 已提交
13
      case subject.class.name
J
James Lopez 已提交
14 15 16 17 18 19 20 21 22 23 24
      when "Project" then project_abilities(user, subject)
      when "Issue" then issue_abilities(user, subject)
      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)
      else []
25 26 27
      end.concat(global_abilities(user))
    end

28 29
    # List of possible abilities for anonymous user
    def anonymous_abilities(user, subject)
D
Douwe Maan 已提交
30 31
      case true
      when subject.is_a?(PersonalSnippet)
32
        anonymous_personal_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)
D
Douwe Maan 已提交
39 40 41
      else
        []
      end
42 43
    end

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

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

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

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

K
Kamil Trzcinski 已提交
75 76 77 78 79 80 81
    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

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

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

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

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

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

S
skv-headless 已提交
114 115
      RequestStore.store[key] ||= begin
        team = project.team
G
gitlabhq 已提交
116

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

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

S
skv-headless 已提交
124
        elsif team.reporter?(user)
125
          rules.push(*project_report_rules)
126

S
skv-headless 已提交
127
        elsif team.guest?(user)
128
          rules.push(*project_guest_rules)
S
skv-headless 已提交
129
        end
130

S
skv-headless 已提交
131
        if project.public? || project.internal?
132
          rules.push(*public_project_rules)
K
Kamil Trzcinski 已提交
133
        end
134

135 136 137
        # Allow to read builds for internal projects
        if project.public? || project.internal?
          rules << :read_build if project.public_builds?
S
skv-headless 已提交
138
        end
139

S
skv-headless 已提交
140
        if project.owner == user || user.admin?
141
          rules.push(*project_admin_rules)
S
skv-headless 已提交
142
        end
143

S
skv-headless 已提交
144
        if project.group && project.group.has_owner?(user)
145
          rules.push(*project_admin_rules)
S
skv-headless 已提交
146
        end
147

S
skv-headless 已提交
148 149 150
        if project.archived?
          rules -= project_archived_rules
        end
151

152
        rules - project_disabled_features_rules(project)
153
      end
154 155
    end

156
    def public_project_rules
J
Jason Lee 已提交
157
      @public_project_rules ||= project_guest_rules + [
158
        :download_code,
159 160
        :fork_project,
        :read_commit_status,
161 162 163
      ]
    end

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

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

194
    def project_dev_rules
J
Jason Lee 已提交
195
      @project_dev_rules ||= project_report_rules + [
196
        :admin_merge_request,
197 198 199 200
        :create_commit_status,
        :update_commit_status,
        :create_build,
        :update_build,
201 202
        :create_merge_request,
        :create_wiki,
203
        :push_code
204 205
      ]
    end
206

207
    def project_archived_rules
J
Jason Lee 已提交
208
      @project_archived_rules ||= [
209
        :create_merge_request,
210 211
        :push_code,
        :push_code_to_protected_branches,
212
        :update_merge_request,
213 214 215 216
        :admin_merge_request
      ]
    end

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

234
    def project_admin_rules
J
Jason Lee 已提交
235
      @project_admin_rules ||= project_master_rules + [
236
        :change_namespace,
237
        :change_visibility_level,
238
        :rename_project,
239
        :remove_project,
240 241
        :archive_project,
        :remove_fork_project
242
      ]
A
Andrey Kumanyaev 已提交
243
    end
G
gitlabhq 已提交
244

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

269 270 271 272
      unless project.builds_enabled
        rules += named_abilities('build')
      end

273 274 275
      rules
    end

276
    def group_abilities(user, group)
277 278
      rules = []

279
      if user.admin? || group.users.include?(user) || ProjectsFinder.new.execute(user, group: group).any?
280 281 282
        rules << :read_group
      end

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

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

      rules.flatten
    end

303
    def namespace_abilities(user, namespace)
304 305
      rules = []

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

      rules.flatten
    end

317
    [:issue, :merge_request].each do |name|
G
gitlabhq 已提交
318
      define_method "#{name}_abilities" do |user, subject|
319 320 321 322
        rules = []

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

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

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

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

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

        rules
G
gitlabhq 已提交
350 351
      end
    end
352

353 354 355 356 357 358 359 360 361 362 363 364
    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 已提交
365
        rules << :read_personal_snippet
366 367 368 369 370
      end

      rules
    end

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

D
Douwe Maan 已提交
376 377
      unless group.last_owner?(target_user)
        can_manage = group_abilities(user, group).include?(:admin_group_member)
378

379
        if can_manage
D
Douwe Maan 已提交
380 381
          rules << :update_group_member
          rules << :destroy_group_member
382
        elsif user == target_user
D
Douwe Maan 已提交
383 384
          rules << :destroy_group_member
        end
385
      end
386

387 388
      rules
    end
C
Ciro Santilli 已提交
389

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

D
Douwe Maan 已提交
395 396
      unless target_user == project.owner
        can_manage = project_abilities(user, project).include?(:admin_project_member)
397

398
        if can_manage
D
Douwe Maan 已提交
399 400
          rules << :update_project_member
          rules << :destroy_project_member
401
        elsif user == target_user
D
Douwe Maan 已提交
402 403
          rules << :destroy_project_member
        end
404
      end
D
Douwe Maan 已提交
405

406 407 408
      rules
    end

K
Kamil Trzcinski 已提交
409 410 411 412 413 414 415
    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

416 417 418 419 420 421 422 423 424 425 426 427
    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
      unless rules.include?(:read_build)
        rules -= [:read_commit_status]
      end
      unless rules.include?(:update_build)
        rules -= [:update_commit_status]
      end
      rules
    end

C
Ciro Santilli 已提交
428 429
    def abilities
      @abilities ||= begin
430 431 432 433
        abilities = Six.new
        abilities << self
        abilities
      end
C
Ciro Santilli 已提交
434
    end
435 436 437 438 439 440

    private

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