Merge lp:~benoit.pierre/sloecode/finer-grained-project-control into lp:sloecode

Proposed by Benoit Pierre
Status: Merged
Merge reported by: Thomi Richards
Merged at revision: not available
Proposed branch: lp:~benoit.pierre/sloecode/finer-grained-project-control
Merge into: lp:sloecode
Diff against target: 234 lines (+77/-21)
5 files modified
sloecode/controllers/project.py (+6/-2)
sloecode/lib/helpers.py (+1/-1)
sloecode/lib/predicates.py (+28/-8)
sloecode/lib/security.py (+40/-10)
sloecode/templates/project-details.html (+2/-0)
To merge this branch: bzr merge lp:~benoit.pierre/sloecode/finer-grained-project-control
Reviewer Review Type Date Requested Status
Thomi Richards (community) Approve
Review via email: mp+77789@code.launchpad.net

Description of the change

More fine-grained control for projects:

- all project members have read access
- only managers and developers have write access

The end result is observers don't have access to branch deletion anymore.

To post a comment you must log in.
Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

Looks good to me, thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'sloecode/controllers/project.py'
2--- sloecode/controllers/project.py 2011-08-15 01:43:23 +0000
3+++ sloecode/controllers/project.py 2011-10-01 14:45:22 +0000
4@@ -11,12 +11,11 @@
5 from sloecode.model.membership import Membership
6 from sloecode.model.site_role import PROJECT_ADMIN
7 from sloecode.model.meta import Session
8-from sloecode.lib.predicates import has_site_role, is_project_member
9+from sloecode.lib.predicates import has_site_role, has_project_read_access, has_project_write_access
10 import sloecode.lib.helpers as h
11
12 log = logging.getLogger(__name__)
13
14-@ControllerProtector(Any(has_site_role(PROJECT_ADMIN), is_project_member()))
15 class ProjectController(BaseController):
16 """ProjectController contains basic admin functionality for the Project
17 model.
18@@ -24,6 +23,7 @@
19 This includes basic CRUD information.
20 """
21
22+ @ControllerProtector(Any(has_site_role(PROJECT_ADMIN), has_project_read_access()))
23 def index(self, project_name):
24 """Render project details page.
25 """
26@@ -46,6 +46,7 @@
27 'user_role': user_role,
28 'repo': repo})
29
30+ @ControllerProtector(Any(has_site_role(PROJECT_ADMIN), has_project_read_access()))
31 def branch_log(self, project_name):
32 projects = Project.get(name=project_name)
33 existing_project = projects[0] if projects else None
34@@ -60,6 +61,7 @@
35 {'project': existing_project,
36 'branch': branch})
37
38+ @ControllerProtector(Any(has_site_role(PROJECT_ADMIN), has_project_write_access()))
39 def delete_branch(self, project_name):
40 """Delete a branch on disk.
41 """
42@@ -73,6 +75,7 @@
43 return redirect(h.url(controller='project', action='index',
44 project_name=project_name))
45
46+ @ControllerProtector(Any(has_site_role(PROJECT_ADMIN), has_project_write_access()))
47 def manage_users(self, project_name):
48 """
49 Add/Remove users from this project.
50@@ -97,6 +100,7 @@
51 {'all_people': all_people,
52 'project_name': project_name})
53
54+ @ControllerProtector(Any(has_site_role(PROJECT_ADMIN), has_project_write_access()))
55 def process_manage_users(self, project_name):
56 """
57 Process the change in the users-in-projects
58
59=== modified file 'sloecode/lib/helpers.py'
60--- sloecode/lib/helpers.py 2011-08-11 08:10:00 +0000
61+++ sloecode/lib/helpers.py 2011-10-01 14:45:22 +0000
62@@ -56,7 +56,7 @@
63
64 # import helper functions that let templates determine if the user has
65 # certain permissions:
66-from sloecode.lib.security import has_site_role, has_project_role
67+from sloecode.lib.security import has_site_role, has_project_role, has_project_access
68 # import site role names as well:
69 from sloecode.model.site_role import USER_ADMIN, PROJECT_ADMIN
70
71
72=== modified file 'sloecode/lib/predicates.py'
73--- sloecode/lib/predicates.py 2011-02-12 08:06:36 +0000
74+++ sloecode/lib/predicates.py 2011-10-01 14:45:22 +0000
75@@ -4,6 +4,7 @@
76 from repoze.what.predicates import Predicate
77
78 import sloecode.lib.security as security
79+from sloecode.model.project import Project
80
81 class has_site_role(Predicate):
82 """Checks whether the logged in user has the desired site role.
83@@ -57,8 +58,8 @@
84 except KeyError:
85 self.unmet()
86
87-class is_project_member(Predicate):
88- """Checks whether the logged in user is a member of the project resource
89+class has_project_read_access(Predicate):
90+ """Checks whether the logged in user as read-only access to the project resource
91 being requested.
92 """
93
94@@ -70,9 +71,28 @@
95 args = self.parse_variables(environ)
96 named_args = args['named_args']
97 project_name = named_args['project_name']
98- for membership in user.projects:
99- if membership.project.name == project_name:
100- return
101- self.unmet()
102- except KeyError:
103- self.unmet()
104+ if security.has_project_access(project_name, user, read_only=True):
105+ return
106+ self.unmet()
107+ except KeyError:
108+ self.unmet()
109+
110+class has_project_write_access(Predicate):
111+ """Checks whether the logged in user as read/write access to the project resource
112+ being requested.
113+ """
114+
115+ message = "You must be a developper/manager of a project in order to have write access."
116+
117+ def evaluate(self, environ, credentials):
118+ try:
119+ user = environ['repoze.who.identity'].get('user')
120+ args = self.parse_variables(environ)
121+ named_args = args['named_args']
122+ project_name = named_args['project_name']
123+ if security.has_project_access(project_name, user, read_only=False):
124+ return
125+ self.unmet()
126+ except KeyError:
127+ self.unmet()
128+
129
130=== modified file 'sloecode/lib/security.py'
131--- sloecode/lib/security.py 2011-01-22 23:30:57 +0000
132+++ sloecode/lib/security.py 2011-10-01 14:45:22 +0000
133@@ -5,6 +5,7 @@
134 from pylons import request
135 from sloecode.model import Person, Membership, Project
136 from sloecode.model.meta import Session
137+from sloecode.model.membership import PR_MANAGER, PR_DEVELOPER
138
139
140 def has_site_role(role_name, user=None):
141@@ -27,11 +28,7 @@
142 return False
143
144
145-def has_project_role(project, role_name, user=None):
146- """Determines whether the currently logged in user has the specified role
147- for a given project. The project can either be the project name (as a
148- string or unicode string), a Project object, or the project id (int).
149- """
150+def _get_project_object(project):
151 prj_obj = None
152 if isinstance(project, Project):
153 prj_obj = project
154@@ -40,17 +37,27 @@
155 if len(projects):
156 prj_obj = projects[0]
157 else:
158- # that ID does not exist. return false:
159- return False
160+ # that ID does not exist.
161+ return None
162 elif isinstance(project, basestring):
163 projects = Project.get(name=project)
164 if len(projects):
165 prj_obj = projects[0]
166 else:
167- # that name does not exist. return false:
168- return False
169+ # that name does not exist.
170+ return None
171 else:
172 raise TypeError("project must be Project,str,unicode, or int type.")
173+ return prj_obj
174+
175+def has_project_role(project, role_name, user=None):
176+ """Determines whether the currently logged in user has the specified role
177+ for a given project. The project can either be the project name (as a
178+ string or unicode string), a Project object, or the project id (int).
179+ """
180+ prj_obj = _get_project_object(project)
181+ if prj_obj is None:
182+ return False
183 try:
184 if not user:
185 e = request.environ
186@@ -61,4 +68,27 @@
187 # any results?
188 return q.count() > 0
189 except KeyError:
190- return False
191\ No newline at end of file
192+ return False
193+
194+def has_project_access(project, user=None, read_only=True):
195+ """Checks whether user as read access to the project resource."""
196+ prj_obj = _get_project_object(project)
197+ if prj_obj is None:
198+ return False
199+ try:
200+ if not user:
201+ e = request.environ
202+ user = e['repoze.who.identity'].get('user')
203+ except KeyError:
204+ return False
205+ for membership in user.projects:
206+ if membership.project.name == prj_obj.name:
207+ if read_only:
208+ # Read only access allowed for all project roles.
209+ return True
210+ if membership.role in [PR_MANAGER, PR_DEVELOPER]:
211+ # Only allow write access for managers and developpers.
212+ return True
213+ return False
214+ return False
215+
216
217=== modified file 'sloecode/templates/project-details.html'
218--- sloecode/templates/project-details.html 2011-08-15 01:43:23 +0000
219+++ sloecode/templates/project-details.html 2011-10-01 14:45:22 +0000
220@@ -50,12 +50,14 @@
221 h.url(controller='project', project_name=project.name,
222 action='branch_log', branch_name=branch.get_name()),
223 title="Branch Log")}}
224+ {% if h.has_project_access(project, read_only=False) %}
225 {{h.link_to(h.image(h.url('/bin-exclamation.png'), 'Delete Branch'),
226 h.url(controller='project', project_name=project.name,
227 action='delete_branch', branch_name=branch.get_name()),
228 title="Delete Branch",
229 confirm="Are you sure you want to delete the '" + branch.get_name()
230 + "' branch? Branch deletion is permanent!")}}
231+ {% endif %}
232 </td>
233 </tr>
234 {% endfor %}

Subscribers

People subscribed via source and target branches