Refactor project membership and private repo functionality into generic protected...
[gitorious:mainline.git] / app / models / committership_authorization.rb
1 # encoding: utf-8
2 #--
3 #   Copyright (C) 2012 Gitorious AS
4 #
5 #   This program is free software: you can redistribute it and/or modify
6 #   it under the terms of the GNU Affero General Public License as published by
7 #   the Free Software Foundation, either version 3 of the License, or
8 #   (at your option) any later version.
9 #
10 #   This program is distributed in the hope that it will be useful,
11 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 #   GNU Affero General Public License for more details.
14 #
15 #   You should have received a copy of the GNU Affero General Public License
16 #   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 #++
18 require "gitorious/authorization/typed_authorization"
19
20 class CommittershipAuthorization < Gitorious::Authorization::TypedAuthorization
21   ### Abilities
22   ability :can_read
23   ability :can_edit
24   ability :can_delete
25   ability :can_grant_access
26
27   def can_read_project?(user, project)
28     can_read_protected_content?(user, project)
29   end
30
31   def can_read_repository?(user, repository)
32     if !repository.project.nil?
33       return false if !can_read_protected_content?(user, repository.project)
34     end
35     can_read_protected_content?(user, repository)
36   end
37
38   def can_read_protected_content?(actor, subject)
39     return true if !private_repos
40     return true if subject.owner == actor
41     return true if subject.content_memberships.count == 0
42     subject.content_memberships.any? { |m| is_member?(actor, m.member) }
43   end
44
45   def can_read_group?(user, group)
46     true
47   end
48
49   def can_read_merge_request?(user, merge_request)
50     can_read_project?(user, merge_request.project)
51   end
52
53   def can_read_user?(actor, user)
54     true
55   end
56
57   def can_read_event?(user, event)
58     can_read_project?(user, event.project) && can_read?(user, event.target)
59   end
60
61   def can_read_favorite?(user, favorite)
62     can_read_project?(user, favorite.project)
63   end
64
65   def can_read_message?(user, message)
66     [message.sender, message.recipient].include?(user)
67   end
68
69   # TODO: Needs more polymorphism
70   def can_push?(user, repository)
71     if repository.wiki?
72       can_write_to_wiki?(user, repository)
73     else
74       committers(repository).include?(user)
75     end
76   end
77
78   def can_delete_project?(candidate, project)
79     admin?(candidate, project) && project.repositories.clones.count == 0
80   end
81
82   def can_delete_repository?(candidate, repository)
83     admin?(candidate, repository)
84   end
85
86   def can_edit_comment?(user, comment)
87     comment.creator?(user) && comment.recently_created?
88   end
89
90   def can_grant_access_project?(candidate, project)
91     private_repos && admin?(candidate, project)
92   end
93
94   def can_request_merge?(user, repository)
95     !repository.mainline? && can_push?(user, repository)
96   end
97
98   def can_resolve_merge_request?(user, merge_request)
99     return false unless user.is_a?(User)
100     return true if user === merge_request.user
101     return reviewers(merge_request.target_repository).include?(user)
102   end
103
104   def can_reopen_merge_request?(user, merge_request)
105     merge_request.can_reopen? && can_resolve_merge_request?(user, merge_request)
106   end
107
108   ### Roles
109
110   def committer?(candidate, repository)
111     candidate.is_a?(User) ? committers(repository).include?(candidate) : false
112   end
113
114   def reviewer?(candidate, repository)
115     candidate.is_a?(User) ? reviewers(repository).include?(candidate) : false
116   end
117
118   def is_member?(candidate, thing)
119     candidate == thing || (thing.respond_to?(:member?) && thing.member?(candidate))
120   end
121
122   ###
123
124   def repository_admin?(candidate, repository)
125     return false if !candidate.is_a?(User)
126     candidate == repository.owner || administrators(repository).include?(candidate)
127   end
128
129   def project_admin?(candidate, project)
130     admin?(candidate, project.owner)
131   end
132
133   def group_admin?(candidate, group)
134     group.user_role(candidate) == Role.admin
135   end
136
137   def group_committer?(candidate, group)
138     [Role.admin, Role.member].include?(group.user_role(candidate))
139   end
140
141   def site_admin?(user)
142     user.is_a?(User) && user.is_admin
143   end
144
145   # returns an array of users who have commit bits to this repository either
146   # directly through the owner, or "indirectly" through the associated
147   # groups
148   def committers(repository)
149     repository.committerships.committers.map{|c| c.members }.flatten.compact.uniq
150   end
151
152   # Returns a list of Users who can review things (as per their Committership)
153   def reviewers(repository)
154     repository.committerships.reviewers.map{|c| c.members }.flatten.compact.uniq
155   end
156
157   # The list of users who can admin this repo, either directly as
158   # committerships or indirectly as members of a group
159   def administrators(repository)
160     repository.committerships.admins.map{|c| c.members }.flatten.compact.uniq
161   end
162
163   def review_repositories(user)
164     user.committerships.reviewers
165   end
166
167   def filter_authorized(actor, collection)
168     return [] if collection.blank?
169     collection.select { |item| can_read?(actor, item) }
170   end
171
172   private
173   def can_write_to_wiki?(user, repository)
174     case repository.wiki_permissions
175     when Repository::WIKI_WRITABLE_EVERYONE
176       return true
177     when Repository::WIKI_WRITABLE_PROJECT_MEMBERS
178       return repository.project.member?(user)
179     end
180   end
181
182   def private_repos
183     GitoriousConfig["enable_private_repositories"]
184   end
185 end