ability.rb 11.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 12 13 14 15 16 17 18 19
      case subject
      when CommitStatus then commit_status_abilities(user, subject)
      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)
J
James Lopez 已提交
20
      else []
21 22 23
      end.concat(global_abilities(user))
    end

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

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

47
      if project && project.public?
48
        rules = [
49 50 51
          :read_project,
          :read_wiki,
          :read_issue,
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

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

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

K
Kamil Trzcinski 已提交
71 72 73 74 75 76 77
    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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

267 268 269
      rules
    end

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

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

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

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

      rules.flatten
    end

297
    def namespace_abilities(user, namespace)
298 299
      rules = []

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

      rules.flatten
    end

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

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

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

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

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

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

        rules
G
gitlabhq 已提交
344 345
      end
    end
346

347 348 349 350 351 352 353 354 355 356 357 358
    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 已提交
359
        rules << :read_personal_snippet
360 361 362 363 364
      end

      rules
    end

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

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

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

381 382
      rules
    end
C
Ciro Santilli 已提交
383

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

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

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

400 401 402
      rules
    end

K
Kamil Trzcinski 已提交
403 404 405 406 407 408 409
    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

410 411 412
    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
413 414
      %w(read create update admin).each do |rule|
        rules -= [:"#{rule}_commit_status"] unless rules.include?(:"#{rule}_build")
415 416 417 418
      end
      rules
    end

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

    private

    def named_abilities(name)
      [
        :"read_#{name}",
432 433
        :"create_#{name}",
        :"update_#{name}",
434 435 436
        :"admin_#{name}"
      ]
    end
G
gitlabhq 已提交
437
  end
G
gitlabhq 已提交
438
end