ability.rb 8.13 KB
Newer Older
gitlabhq's avatar
gitlabhq committed
1
class Ability
Andrey Kumanyaev's avatar
Andrey Kumanyaev committed
2
  class << self
3
    def allowed(user, subject)
4
      return not_auth_abilities(user, subject) if user.nil?
5
      return [] unless user.kind_of?(User)
6
      return [] if user.blocked?
7

Andrey Kumanyaev's avatar
Andrey Kumanyaev committed
8
      case subject.class.name
9 10 11
      when "Project" then project_abilities(user, subject)
      when "Issue" then issue_abilities(user, subject)
      when "Note" then note_abilities(user, subject)
12
      when "ProjectSnippet" then project_snippet_abilities(user, subject)
13
      when "PersonalSnippet" then personal_snippet_abilities(user, subject)
14
      when "MergeRequest" then merge_request_abilities(user, subject)
15 16
      when "Group" then group_abilities(user, subject)
      when "Namespace" then namespace_abilities(user, subject)
17
      when "GroupMember" then group_member_abilities(user, subject)
Andrey Kumanyaev's avatar
Andrey Kumanyaev committed
18
      else []
19 20 21
      end.concat(global_abilities(user))
    end

22 23 24 25 26 27 28 29 30 31 32
    # List of possible abilities
    # for non-authenticated user
    def not_auth_abilities(user, subject)
      project = if subject.kind_of?(Project)
                  subject
                elsif subject.respond_to?(:project)
                  subject.project
                else
                  nil
                end

33
      if project && project.public?
34
        rules = [
35 36 37
          :read_project,
          :read_wiki,
          :read_issue,
38
          :read_label,
39 40
          :read_milestone,
          :read_project_snippet,
41
          :read_project_member,
42 43
          :read_merge_request,
          :read_note,
Kamil Trzcinski's avatar
Kamil Trzcinski committed
44
          :read_build,
45 46
          :download_code
        ]
47 48

        rules - project_disabled_features_rules(project)
49
      else
50 51 52 53 54 55 56
        group = if subject.kind_of?(Group)
                  subject
                elsif subject.respond_to?(:group)
                  subject.group
                else
                  nil
                end
57

58
        if group && group.public_profile?
59 60 61 62
          [:read_group]
        else
          []
        end
63 64 65
      end
    end

66 67 68 69
    def global_abilities(user)
      rules = []
      rules << :create_group if user.can_create_group
      rules
gitlabhq's avatar
gitlabhq committed
70 71
    end

Andrey Kumanyaev's avatar
Andrey Kumanyaev committed
72 73
    def project_abilities(user, project)
      rules = []
74
      key = "/user/#{user.id}/project/#{project.id}"
75

76 77
      RequestStore.store[key] ||= begin
        team = project.team
gitlabhq's avatar
gitlabhq committed
78

79 80
        # Rules based on role in project
        if team.master?(user)
81
          rules.push(*project_master_rules)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
82

83
        elsif team.developer?(user)
84
          rules.push(*project_dev_rules)
85

86
        elsif team.reporter?(user)
87
          rules.push(*project_report_rules)
88

89
        elsif team.guest?(user)
90
          rules.push(*project_guest_rules)
91
        end
92

93
        if project.public? || project.internal?
94
          rules.push(*public_project_rules)
95
        end
96

97
        if project.owner == user || user.admin?
98
          rules.push(*project_admin_rules)
99
        end
100

101
        if project.group && project.group.has_owner?(user)
102
          rules.push(*project_admin_rules)
103
        end
104

105 106 107
        if project.archived?
          rules -= project_archived_rules
        end
108

109
        rules - project_disabled_features_rules(project)
110
      end
111 112
    end

113
    def public_project_rules
114
      project_guest_rules + [
115
        :download_code,
116
        :fork_project
117 118 119
      ]
    end

120 121
    def project_guest_rules
      [
Andrey Kumanyaev's avatar
Andrey Kumanyaev committed
122 123 124
        :read_project,
        :read_wiki,
        :read_issue,
125
        :read_label,
Andrey Kumanyaev's avatar
Andrey Kumanyaev committed
126
        :read_milestone,
Andrew8xx8's avatar
Andrew8xx8 committed
127
        :read_project_snippet,
128
        :read_project_member,
Andrey Kumanyaev's avatar
Andrey Kumanyaev committed
129 130
        :read_merge_request,
        :read_note,
Kamil Trzcinski's avatar
Kamil Trzcinski committed
131
        :read_build,
132 133 134
        :create_project,
        :create_issue,
        :create_note
135 136
      ]
    end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
137

138 139
    def project_report_rules
      project_guest_rules + [
140 141
        :create_commit_status,
        :read_commit_statuses,
Andrey Kumanyaev's avatar
Andrey Kumanyaev committed
142
        :download_code,
143
        :fork_project,
144 145 146
        :create_project_snippet,
        :update_issue,
        :admin_issue,
147
        :admin_label
148 149
      ]
    end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
150

151 152
    def project_dev_rules
      project_report_rules + [
153
        :admin_merge_request,
154 155
        :create_merge_request,
        :create_wiki,
156
        :manage_builds,
157
        :download_build_artifacts,
158
        :push_code
159 160
      ]
    end
161

162 163
    def project_archived_rules
      [
164
        :create_merge_request,
165 166
        :push_code,
        :push_code_to_protected_branches,
167
        :update_merge_request,
168 169 170 171
        :admin_merge_request
      ]
    end

172 173 174
    def project_master_rules
      project_dev_rules + [
        :push_code_to_protected_branches,
175 176
        :update_project_snippet,
        :update_merge_request,
Andrey Kumanyaev's avatar
Andrey Kumanyaev committed
177
        :admin_milestone,
Andrew8xx8's avatar
Andrew8xx8 committed
178
        :admin_project_snippet,
179
        :admin_project_member,
Andrey Kumanyaev's avatar
Andrey Kumanyaev committed
180 181
        :admin_merge_request,
        :admin_note,
182 183
        :admin_wiki,
        :admin_project
184 185
      ]
    end
gitlabhq's avatar
gitlabhq committed
186

187 188
    def project_admin_rules
      project_master_rules + [
189
        :change_namespace,
190
        :change_visibility_level,
191
        :rename_project,
192
        :remove_project,
193 194
        :archive_project,
        :remove_fork_project
195
      ]
Andrey Kumanyaev's avatar
Andrey Kumanyaev committed
196
    end
gitlabhq's avatar
gitlabhq committed
197

198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
    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

225
    def group_abilities(user, group)
226 227
      rules = []

228
      if user.admin? || group.users.include?(user) || ProjectsFinder.new.execute(user, group: group).any?
229 230 231
        rules << :read_group
      end

232 233
      # Only group masters and group owners can create new projects in group
      if group.has_master?(user) || group.has_owner?(user) || user.admin?
234
        rules.push(*[
235
          :create_projects,
236
          :admin_milestones
237
        ])
238 239
      end

240
      # Only group owner and administrators can admin group
241
      if group.has_owner?(user) || user.admin?
242
        rules.push(*[
243
          :admin_group,
244 245
          :admin_namespace,
          :admin_group_member
246
        ])
247
      end
248 249 250 251

      rules.flatten
    end

252
    def namespace_abilities(user, namespace)
253 254
      rules = []

255
      # Only namespace owner and administrators can admin it
256
      if namespace.owner == user || user.admin?
257
        rules.push(*[
258
          :create_projects,
259
          :admin_namespace
260
        ])
261 262 263 264 265
      end

      rules.flatten
    end

266 267

    [:issue, :merge_request].each do |name|
gitlabhq's avatar
gitlabhq committed
268
      define_method "#{name}_abilities" do |user, subject|
269 270 271 272
        rules = []

        if subject.author == user || (subject.respond_to?(:assignee) && subject.assignee == user)
          rules += [
gitlabhq's avatar
gitlabhq committed
273
            :"read_#{name}",
274
            :"update_#{name}",
gitlabhq's avatar
gitlabhq committed
275
          ]
276 277 278 279 280 281 282 283 284 285 286 287 288
        end

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

    [:note, :project_snippet, :personal_snippet].each do |name|
      define_method "#{name}_abilities" do |user, subject|
        rules = []

        if subject.author == user
          rules += [
289
            :"read_#{name}",
290
            :"update_#{name}",
291
            :"admin_#{name}"
292
          ]
gitlabhq's avatar
gitlabhq committed
293
        end
294 295 296 297 298 299

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

        rules
gitlabhq's avatar
gitlabhq committed
300 301
      end
    end
302

303
    def group_member_abilities(user, subject)
304 305 306
      rules = []
      target_user = subject.user
      group = subject.group
307
      can_manage = group_abilities(user, group).include?(:admin_group_member)
308

309
      if can_manage && (user != target_user)
310
        rules << :update_group_member
311
        rules << :destroy_group_member
312
      end
313

314
      if !group.last_owner?(user) && (can_manage || (user == target_user))
315
        rules << :destroy_group_member
316
      end
317

318 319
      rules
    end
Ciro Santilli's avatar
Ciro Santilli committed
320 321 322 323 324 325 326 327

    def abilities
      @abilities ||= begin
                       abilities = Six.new
                       abilities << self
                       abilities
                     end
    end
328 329 330 331 332 333

    private

    def named_abilities(name)
      [
        :"read_#{name}",
334 335
        :"create_#{name}",
        :"update_#{name}",
336 337 338
        :"admin_#{name}"
      ]
    end
gitlabhq's avatar
gitlabhq committed
339
  end
gitlabhq's avatar
gitlabhq committed
340
end