BigW Consortium Gitlab

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

Andrey Kumanyaev committed
8
      case subject.class.name
James Lopez committed
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)
Douwe Maan committed
25 26
      case true
      when subject.is_a?(PersonalSnippet)
27
        anonymous_personal_snippet_abilities(subject)
Douwe Maan committed
28
      when subject.is_a?(Project) || subject.respond_to?(:project)
29
        anonymous_project_abilities(subject)
Douwe Maan committed
30
      when subject.is_a?(Group) || subject.respond_to?(:group)
31
        anonymous_group_abilities(subject)
Douwe Maan committed
32 33 34
      else
        []
      end
35 36
    end

37
    def anonymous_project_abilities(subject)
Douwe Maan committed
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,
Kamil Trzcinski committed
55
          :read_build,
56 57
          :download_code
        ]
58 59

        rules - project_disabled_features_rules(project)
60
      else
61 62 63
        []
      end
    end
64

65
    def anonymous_group_abilities(subject)
Douwe Maan committed
66
      group = if subject.is_a?(Group)
67 68 69 70 71 72 73 74 75 76 77 78
                subject
              else
                subject.group
              end

      if group && group.public_profile?
        [:read_group]
      else
        []
      end
    end

79
    def anonymous_personal_snippet_abilities(snippet)
80 81 82 83
      if snippet.public?
        [:read_personal_snippet]
      else
        []
84 85 86
      end
    end

87 88 89 90
    def global_abilities(user)
      rules = []
      rules << :create_group if user.can_create_group
      rules
gitlabhq committed
91 92
    end

Andrey Kumanyaev committed
93 94
    def project_abilities(user, project)
      rules = []
95
      key = "/user/#{user.id}/project/#{project.id}"
96

97 98
      RequestStore.store[key] ||= begin
        team = project.team
gitlabhq committed
99

100 101
        # Rules based on role in project
        if team.master?(user)
102
          rules.push(*project_master_rules)
103

104
        elsif team.developer?(user)
105
          rules.push(*project_dev_rules)
106

107
        elsif team.reporter?(user)
108
          rules.push(*project_report_rules)
109

110
        elsif team.guest?(user)
111
          rules.push(*project_guest_rules)
112
        end
113

114
        if project.public? || project.internal?
115
          rules.push(*public_project_rules)
116
        end
117

118
        if project.owner == user || user.admin?
119
          rules.push(*project_admin_rules)
120
        end
121

122
        if project.group && project.group.has_owner?(user)
123
          rules.push(*project_admin_rules)
124
        end
125

126 127 128
        if project.archived?
          rules -= project_archived_rules
        end
129

130
        rules - project_disabled_features_rules(project)
131
      end
132 133
    end

134
    def public_project_rules
135
      @public_project_rules ||= project_guest_rules + [
136
        :download_code,
137
        :fork_project
138 139 140
      ]
    end

141
    def project_guest_rules
142
      @project_guest_rules ||= [
Andrey Kumanyaev committed
143 144 145
        :read_project,
        :read_wiki,
        :read_issue,
146
        :read_label,
Andrey Kumanyaev committed
147
        :read_milestone,
Andrew8xx8 committed
148
        :read_project_snippet,
149
        :read_project_member,
Andrey Kumanyaev committed
150 151
        :read_merge_request,
        :read_note,
Kamil Trzcinski committed
152
        :read_build,
153 154 155
        :create_project,
        :create_issue,
        :create_note
156 157
      ]
    end
Dmitriy Zaporozhets committed
158

159
    def project_report_rules
160
      @project_report_rules ||= project_guest_rules + [
161 162
        :create_commit_status,
        :read_commit_statuses,
Andrey Kumanyaev committed
163
        :download_code,
164
        :fork_project,
165 166 167
        :create_project_snippet,
        :update_issue,
        :admin_issue,
168
        :admin_label
169 170
      ]
    end
Dmitriy Zaporozhets committed
171

172
    def project_dev_rules
173
      @project_dev_rules ||= project_report_rules + [
174
        :admin_merge_request,
175 176
        :create_merge_request,
        :create_wiki,
177
        :manage_builds,
178
        :download_build_artifacts,
179
        :push_code
180 181
      ]
    end
182

183
    def project_archived_rules
184
      @project_archived_rules ||= [
185
        :create_merge_request,
186 187
        :push_code,
        :push_code_to_protected_branches,
188
        :update_merge_request,
189 190 191 192
        :admin_merge_request
      ]
    end

193
    def project_master_rules
194
      @project_master_rules ||= project_dev_rules + [
195
        :push_code_to_protected_branches,
196 197
        :update_project_snippet,
        :update_merge_request,
Andrey Kumanyaev committed
198
        :admin_milestone,
Andrew8xx8 committed
199
        :admin_project_snippet,
200
        :admin_project_member,
Andrey Kumanyaev committed
201 202
        :admin_merge_request,
        :admin_note,
203 204
        :admin_wiki,
        :admin_project
205 206
      ]
    end
gitlabhq committed
207

208
    def project_admin_rules
209
      @project_admin_rules ||= project_master_rules + [
210
        :change_namespace,
211
        :change_visibility_level,
212
        :rename_project,
213
        :remove_project,
214 215
        :archive_project,
        :remove_fork_project
216
      ]
Andrey Kumanyaev committed
217
    end
gitlabhq committed
218

219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
    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

      rules
    end

246
    def group_abilities(user, group)
247 248
      rules = []

249
      if user.admin? || group.users.include?(user) || ProjectsFinder.new.execute(user, group: group).any?
250 251 252
        rules << :read_group
      end

253 254
      # Only group masters and group owners can create new projects in group
      if group.has_master?(user) || group.has_owner?(user) || user.admin?
255
        rules += [
256
          :create_projects,
257
          :admin_milestones
258
        ]
259 260
      end

261
      # Only group owner and administrators can admin group
262
      if group.has_owner?(user) || user.admin?
Douwe Maan committed
263 264 265 266 267
        rules += [
          :admin_group,
          :admin_namespace,
          :admin_group_member
        ]
268
      end
269 270 271 272

      rules.flatten
    end

273
    def namespace_abilities(user, namespace)
274 275
      rules = []

276
      # Only namespace owner and administrators can admin it
277
      if namespace.owner == user || user.admin?
Douwe Maan committed
278 279 280 281
        rules += [
          :create_projects,
          :admin_namespace
        ]
282 283 284 285 286
      end

      rules.flatten
    end

287
    [:issue, :merge_request].each do |name|
gitlabhq committed
288
      define_method "#{name}_abilities" do |user, subject|
289 290 291 292
        rules = []

        if subject.author == user || (subject.respond_to?(:assignee) && subject.assignee == user)
          rules += [
gitlabhq committed
293
            :"read_#{name}",
294
            :"update_#{name}",
gitlabhq committed
295
          ]
296 297 298 299 300 301 302
        end

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

303
    [:note, :project_snippet].each do |name|
304 305 306 307 308
      define_method "#{name}_abilities" do |user, subject|
        rules = []

        if subject.author == user
          rules += [
309
            :"read_#{name}",
310
            :"update_#{name}",
311
            :"admin_#{name}"
312
          ]
gitlabhq committed
313
        end
314 315 316 317 318 319

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

        rules
gitlabhq committed
320 321
      end
    end
322

323 324 325 326 327 328 329 330 331 332 333 334
    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?
335
        rules << :read_personal_snippet
336 337 338 339 340
      end

      rules
    end

341
    def group_member_abilities(user, subject)
342 343 344
      rules = []
      target_user = subject.user
      group = subject.group
345

Douwe Maan committed
346 347
      unless group.last_owner?(target_user)
        can_manage = group_abilities(user, group).include?(:admin_group_member)
348

349
        if can_manage
Douwe Maan committed
350 351
          rules << :update_group_member
          rules << :destroy_group_member
352
        elsif user == target_user
Douwe Maan committed
353 354
          rules << :destroy_group_member
        end
355
      end
356

357 358
      rules
    end
Ciro Santilli committed
359

360 361 362 363 364
    def project_member_abilities(user, subject)
      rules = []
      target_user = subject.user
      project = subject.project

Douwe Maan committed
365 366
      unless target_user == project.owner
        can_manage = project_abilities(user, project).include?(:admin_project_member)
367

368
        if can_manage
Douwe Maan committed
369 370
          rules << :update_project_member
          rules << :destroy_project_member
371
        elsif user == target_user
Douwe Maan committed
372 373
          rules << :destroy_project_member
        end
374
      end
Douwe Maan committed
375

376 377 378
      rules
    end

Ciro Santilli committed
379 380
    def abilities
      @abilities ||= begin
381 382 383 384
        abilities = Six.new
        abilities << self
        abilities
      end
Ciro Santilli committed
385
    end
386 387 388 389 390 391

    private

    def named_abilities(name)
      [
        :"read_#{name}",
392 393
        :"create_#{name}",
        :"update_#{name}",
394 395 396
        :"admin_#{name}"
      ]
    end
gitlabhq committed
397
  end
gitlabhq committed
398
end