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

A
Andrey Kumanyaev 已提交
8
      case subject.class.name
J
James Lopez 已提交
9 10 11 12 13 14 15 16 17 18 19
      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 []
20 21 22
      end.concat(global_abilities(user))
    end

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

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

44
      if project && project.public?
45
        rules = [
46 47 48
          :read_project,
          :read_wiki,
          :read_issue,
49
          :read_label,
50 51
          :read_milestone,
          :read_project_snippet,
52
          :read_project_member,
53 54
          :read_merge_request,
          :read_note,
55
          :read_commit_status,
56 57
          :download_code
        ]
58

59
        if project.allow_guest_to_access_builds?
K
Kamil Trzcinski 已提交
60
          rules << :read_build
61 62
        end

63
        rules - project_disabled_features_rules(project)
64
      else
65 66 67
        []
      end
    end
68

69
    def anonymous_group_abilities(subject)
D
Douwe Maan 已提交
70
      group = if subject.is_a?(Group)
71 72 73 74 75
                subject
              else
                subject.group
              end

76
      if group && group.projects.public_only.any?
77 78 79 80 81 82
        [:read_group]
      else
        []
      end
    end

83
    def anonymous_personal_snippet_abilities(snippet)
84 85 86 87
      if snippet.public?
        [:read_personal_snippet]
      else
        []
88 89 90
      end
    end

91 92 93 94
    def global_abilities(user)
      rules = []
      rules << :create_group if user.can_create_group
      rules
G
gitlabhq 已提交
95 96
    end

A
Andrey Kumanyaev 已提交
97 98
    def project_abilities(user, project)
      rules = []
S
skv-headless 已提交
99
      key = "/user/#{user.id}/project/#{project.id}"
100

S
skv-headless 已提交
101 102
      RequestStore.store[key] ||= begin
        team = project.team
G
gitlabhq 已提交
103

S
skv-headless 已提交
104 105
        # Rules based on role in project
        if team.master?(user)
106
          rules.push(*project_master_rules)
D
Dmitriy Zaporozhets 已提交
107

S
skv-headless 已提交
108
        elsif team.developer?(user)
109
          rules.push(*project_dev_rules)
110

S
skv-headless 已提交
111
        elsif team.reporter?(user)
112
          rules.push(*project_report_rules)
113

S
skv-headless 已提交
114
        elsif team.guest?(user)
115
          rules.push(*project_guest_rules)
116 117

          if project.allow_guest_to_access_builds?
K
Kamil Trzcinski 已提交
118
            rules << :read_build
119
          end
S
skv-headless 已提交
120
        end
121

S
skv-headless 已提交
122
        if project.public? || project.internal?
123
          rules.push(*public_project_rules)
124

125
          if project.allow_guest_to_access_builds?
K
Kamil Trzcinski 已提交
126
            rules << :read_build
127
          end
S
skv-headless 已提交
128
        end
129

S
skv-headless 已提交
130
        if project.owner == user || user.admin?
131
          rules.push(*project_admin_rules)
S
skv-headless 已提交
132
        end
133

S
skv-headless 已提交
134
        if project.group && project.group.has_owner?(user)
135
          rules.push(*project_admin_rules)
S
skv-headless 已提交
136
        end
137

S
skv-headless 已提交
138 139 140
        if project.archived?
          rules -= project_archived_rules
        end
141

142
        rules - project_disabled_features_rules(project)
143
      end
144 145
    end

146
    def public_project_rules
J
Jason Lee 已提交
147
      @public_project_rules ||= project_guest_rules + [
148
        :download_code,
149 150
        :fork_project,
        :read_commit_status,
151 152 153
      ]
    end

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

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

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

197
    def project_archived_rules
J
Jason Lee 已提交
198
      @project_archived_rules ||= [
199
        :create_merge_request,
200 201
        :push_code,
        :push_code_to_protected_branches,
202
        :update_merge_request,
203 204 205 206
        :admin_merge_request
      ]
    end

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

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

235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
    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

259 260 261 262
      unless project.builds_enabled
        rules += named_abilities('build')
      end

263 264 265
      rules
    end

266
    def group_abilities(user, group)
267 268
      rules = []

269
      if user.admin? || group.users.include?(user) || ProjectsFinder.new.execute(user, group: group).any?
270 271 272
        rules << :read_group
      end

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

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

      rules.flatten
    end

293
    def namespace_abilities(user, namespace)
294 295
      rules = []

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

      rules.flatten
    end

307
    [:issue, :merge_request].each do |name|
G
gitlabhq 已提交
308
      define_method "#{name}_abilities" do |user, subject|
309 310 311 312
        rules = []

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

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

323
    [:note, :project_snippet].each do |name|
324 325 326 327 328
      define_method "#{name}_abilities" do |user, subject|
        rules = []

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

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

        rules
G
gitlabhq 已提交
340 341
      end
    end
342

343 344 345 346 347 348 349 350 351 352 353 354
    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 已提交
355
        rules << :read_personal_snippet
356 357 358 359 360
      end

      rules
    end

361
    def group_member_abilities(user, subject)
362 363 364
      rules = []
      target_user = subject.user
      group = subject.group
365

D
Douwe Maan 已提交
366 367
      unless group.last_owner?(target_user)
        can_manage = group_abilities(user, group).include?(:admin_group_member)
368

369
        if can_manage
D
Douwe Maan 已提交
370 371
          rules << :update_group_member
          rules << :destroy_group_member
372
        elsif user == target_user
D
Douwe Maan 已提交
373 374
          rules << :destroy_group_member
        end
375
      end
376

377 378
      rules
    end
C
Ciro Santilli 已提交
379

380 381 382 383 384
    def project_member_abilities(user, subject)
      rules = []
      target_user = subject.user
      project = subject.project

D
Douwe Maan 已提交
385 386
      unless target_user == project.owner
        can_manage = project_abilities(user, project).include?(:admin_project_member)
387

388
        if can_manage
D
Douwe Maan 已提交
389 390
          rules << :update_project_member
          rules << :destroy_project_member
391
        elsif user == target_user
D
Douwe Maan 已提交
392 393
          rules << :destroy_project_member
        end
394
      end
D
Douwe Maan 已提交
395

396 397 398
      rules
    end

C
Ciro Santilli 已提交
399 400
    def abilities
      @abilities ||= begin
401 402 403 404
        abilities = Six.new
        abilities << self
        abilities
      end
C
Ciro Santilli 已提交
405
    end
406 407 408 409 410 411

    private

    def named_abilities(name)
      [
        :"read_#{name}",
412 413
        :"create_#{name}",
        :"update_#{name}",
414 415 416
        :"admin_#{name}"
      ]
    end
G
gitlabhq 已提交
417
  end
G
gitlabhq 已提交
418
end