##// END OF EJS Templates
forbid removing yourself as beeing an admin of a group
marcink -
r3332:92dfc033 beta
parent child Browse files
Show More
@@ -1,378 +1,398 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.repos_groups
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Repositories groups controller for RhodeCode
7 7
8 8 :created_on: Mar 23, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28 import formencode
29 29
30 30 from formencode import htmlfill
31 31
32 32 from pylons import request, tmpl_context as c, url
33 33 from pylons.controllers.util import abort, redirect
34 34 from pylons.i18n.translation import _
35 35
36 36 from sqlalchemy.exc import IntegrityError
37 37
38 38 import rhodecode
39 39 from rhodecode.lib import helpers as h
40 40 from rhodecode.lib.ext_json import json
41 41 from rhodecode.lib.auth import LoginRequired, HasPermissionAnyDecorator,\
42 42 HasReposGroupPermissionAnyDecorator, HasReposGroupPermissionAll,\
43 43 HasPermissionAll
44 44 from rhodecode.lib.base import BaseController, render
45 45 from rhodecode.model.db import RepoGroup, Repository
46 46 from rhodecode.model.repos_group import ReposGroupModel
47 47 from rhodecode.model.forms import ReposGroupForm
48 48 from rhodecode.model.meta import Session
49 49 from rhodecode.model.repo import RepoModel
50 50 from webob.exc import HTTPInternalServerError, HTTPNotFound
51 51 from rhodecode.lib.utils2 import str2bool, safe_int
52 52 from sqlalchemy.sql.expression import func
53 53 from rhodecode.model.scm import GroupList
54 54
55 55 log = logging.getLogger(__name__)
56 56
57 57
58 58 class ReposGroupsController(BaseController):
59 59 """REST Controller styled on the Atom Publishing Protocol"""
60 60 # To properly map this controller, ensure your config/routing.py
61 61 # file has a resource setup:
62 62 # map.resource('repos_group', 'repos_groups')
63 63
64 64 @LoginRequired()
65 65 def __before__(self):
66 66 super(ReposGroupsController, self).__before__()
67 67
68 68 def __load_defaults(self, allow_empty_group=False, exclude_group_ids=[]):
69 69 if HasPermissionAll('hg.admin')('group edit'):
70 70 #we're global admin, we're ok and we can create TOP level groups
71 71 allow_empty_group = True
72 72
73 73 #override the choices for this form, we need to filter choices
74 74 #and display only those we have ADMIN right
75 75 groups_with_admin_rights = GroupList(RepoGroup.query().all(),
76 76 perm_set=['group.admin'])
77 77 c.repo_groups = RepoGroup.groups_choices(groups=groups_with_admin_rights,
78 78 show_empty_group=allow_empty_group)
79 79 # exclude filtered ids
80 80 c.repo_groups = filter(lambda x: x[0] not in exclude_group_ids,
81 81 c.repo_groups)
82 82 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
83 83 repo_model = RepoModel()
84 84 c.users_array = repo_model.get_users_js()
85 85 c.users_groups_array = repo_model.get_users_groups_js()
86 86
87 87 def __load_data(self, group_id):
88 88 """
89 89 Load defaults settings for edit, and update
90 90
91 91 :param group_id:
92 92 """
93 93 repo_group = RepoGroup.get_or_404(group_id)
94 94 data = repo_group.get_dict()
95 95 data['group_name'] = repo_group.name
96 96
97 97 # fill repository users
98 98 for p in repo_group.repo_group_to_perm:
99 99 data.update({'u_perm_%s' % p.user.username:
100 100 p.permission.permission_name})
101 101
102 102 # fill repository groups
103 103 for p in repo_group.users_group_to_perm:
104 104 data.update({'g_perm_%s' % p.users_group.users_group_name:
105 105 p.permission.permission_name})
106 106
107 107 return data
108 108
109 def _revoke_perms_on_yourself(self, form_result):
110 _up = filter(lambda u: c.rhodecode_user.username == u[0],
111 form_result['perms_updates'])
112 _new = filter(lambda u: c.rhodecode_user.username == u[0],
113 form_result['perms_new'])
114 if _new and _new[0][1] != 'group.admin' or _up and _up[0][1] != 'group.admin':
115 return True
116 return False
117
109 118 def index(self, format='html'):
110 119 """GET /repos_groups: All items in the collection"""
111 120 # url('repos_groups')
112 121 group_iter = GroupList(RepoGroup.query().all(), perm_set=['group.admin'])
113 122 sk = lambda g: g.parents[0].group_name if g.parents else g.group_name
114 123 c.groups = sorted(group_iter, key=sk)
115 124 return render('admin/repos_groups/repos_groups_show.html')
116 125
117 126 def create(self):
118 127 """POST /repos_groups: Create a new item"""
119 128 # url('repos_groups')
120 129
121 130 self.__load_defaults()
122 131
123 132 # permissions for can create group based on parent_id are checked
124 133 # here in the Form
125 134 repos_group_form = ReposGroupForm(available_groups=
126 135 map(lambda k: unicode(k[0]), c.repo_groups))()
127 136 try:
128 137 form_result = repos_group_form.to_python(dict(request.POST))
129 138 ReposGroupModel().create(
130 139 group_name=form_result['group_name'],
131 140 group_description=form_result['group_description'],
132 141 parent=form_result['group_parent_id'],
133 142 owner=self.rhodecode_user.user_id
134 143 )
135 144 Session().commit()
136 145 h.flash(_('created repos group %s') \
137 146 % form_result['group_name'], category='success')
138 147 #TODO: in futureaction_logger(, '', '', '', self.sa)
139 148 except formencode.Invalid, errors:
140 149 return htmlfill.render(
141 150 render('admin/repos_groups/repos_groups_add.html'),
142 151 defaults=errors.value,
143 152 errors=errors.error_dict or {},
144 153 prefix_error=False,
145 154 encoding="UTF-8")
146 155 except Exception:
147 156 log.error(traceback.format_exc())
148 157 h.flash(_('error occurred during creation of repos group %s') \
149 158 % request.POST.get('group_name'), category='error')
150 159 parent_group_id = form_result['group_parent_id']
151 160 #TODO: maybe we should get back to the main view, not the admin one
152 161 return redirect(url('repos_groups', parent_group=parent_group_id))
153 162
154 163 def new(self, format='html'):
155 164 """GET /repos_groups/new: Form to create a new item"""
156 165 # url('new_repos_group')
157 166 if HasPermissionAll('hg.admin')('group create'):
158 167 #we're global admin, we're ok and we can create TOP level groups
159 168 pass
160 169 else:
161 170 # we pass in parent group into creation form, thus we know
162 171 # what would be the group, we can check perms here !
163 172 group_id = safe_int(request.GET.get('parent_group'))
164 173 group = RepoGroup.get(group_id) if group_id else None
165 174 group_name = group.group_name if group else None
166 175 if HasReposGroupPermissionAll('group.admin')(group_name, 'group create'):
167 176 pass
168 177 else:
169 178 return abort(403)
170 179
171 180 self.__load_defaults()
172 181 return render('admin/repos_groups/repos_groups_add.html')
173 182
174 183 @HasReposGroupPermissionAnyDecorator('group.admin')
175 184 def update(self, group_name):
176 185 """PUT /repos_groups/group_name: Update an existing item"""
177 186 # Forms posted to this method should contain a hidden field:
178 187 # <input type="hidden" name="_method" value="PUT" />
179 188 # Or using helpers:
180 189 # h.form(url('repos_group', group_name=GROUP_NAME),
181 190 # method='put')
182 191 # url('repos_group', group_name=GROUP_NAME)
183 192
184 193 c.repos_group = ReposGroupModel()._get_repos_group(group_name)
185 194 if HasPermissionAll('hg.admin')('group edit'):
186 195 #we're global admin, we're ok and we can create TOP level groups
187 196 allow_empty_group = True
188 197 elif not c.repos_group.parent_group:
189 198 allow_empty_group = True
190 199 else:
191 200 allow_empty_group = False
192 201 self.__load_defaults(allow_empty_group=allow_empty_group,
193 202 exclude_group_ids=[c.repos_group.group_id])
194 203
195 204 repos_group_form = ReposGroupForm(
196 205 edit=True,
197 206 old_data=c.repos_group.get_dict(),
198 207 available_groups=c.repo_groups_choices,
199 208 can_create_in_root=allow_empty_group,
200 209 )()
201 210 try:
202 211 form_result = repos_group_form.to_python(dict(request.POST))
212 if not c.rhodecode_user.is_admin:
213 if self._revoke_perms_on_yourself(form_result):
214 msg = _('Cannot revoke permission for yourself as admin')
215 h.flash(msg, category='warning')
216 raise Exception('revoke admin permission on self')
217
203 218 new_gr = ReposGroupModel().update(group_name, form_result)
204 219 Session().commit()
205 220 h.flash(_('updated repos group %s') \
206 221 % form_result['group_name'], category='success')
207 222 # we now have new name !
208 223 group_name = new_gr.group_name
209 224 #TODO: in future action_logger(, '', '', '', self.sa)
210 225 except formencode.Invalid, errors:
211 226
212 227 return htmlfill.render(
213 228 render('admin/repos_groups/repos_groups_edit.html'),
214 229 defaults=errors.value,
215 230 errors=errors.error_dict or {},
216 231 prefix_error=False,
217 232 encoding="UTF-8")
218 233 except Exception:
219 234 log.error(traceback.format_exc())
220 235 h.flash(_('error occurred during update of repos group %s') \
221 236 % request.POST.get('group_name'), category='error')
222 237
223 238 return redirect(url('edit_repos_group', group_name=group_name))
224 239
225 240 @HasReposGroupPermissionAnyDecorator('group.admin')
226 241 def delete(self, group_name):
227 242 """DELETE /repos_groups/group_name: Delete an existing item"""
228 243 # Forms posted to this method should contain a hidden field:
229 244 # <input type="hidden" name="_method" value="DELETE" />
230 245 # Or using helpers:
231 246 # h.form(url('repos_group', group_name=GROUP_NAME),
232 247 # method='delete')
233 248 # url('repos_group', group_name=GROUP_NAME)
234 249
235 250 gr = c.repos_group = ReposGroupModel()._get_repos_group(group_name)
236 251 repos = gr.repositories.all()
237 252 if repos:
238 253 h.flash(_('This group contains %s repositores and cannot be '
239 254 'deleted') % len(repos),
240 255 category='error')
241 256 return redirect(url('repos_groups'))
242 257
243 258 try:
244 259 ReposGroupModel().delete(group_name)
245 260 Session().commit()
246 261 h.flash(_('removed repos group %s') % gr.group_name,
247 262 category='success')
248 263 #TODO: in future action_logger(, '', '', '', self.sa)
249 264 except IntegrityError, e:
250 265 if str(e.message).find('groups_group_parent_id_fkey') != -1:
251 266 log.error(traceback.format_exc())
252 267 h.flash(_('Cannot delete this group it still contains '
253 268 'subgroups'),
254 269 category='warning')
255 270 else:
256 271 log.error(traceback.format_exc())
257 272 h.flash(_('error occurred during deletion of repos '
258 273 'group %s') % gr.group_name, category='error')
259 274
260 275 except Exception:
261 276 log.error(traceback.format_exc())
262 277 h.flash(_('error occurred during deletion of repos '
263 278 'group %s') % gr.group_name, category='error')
264 279
265 280 return redirect(url('repos_groups'))
266 281
267 282 @HasReposGroupPermissionAnyDecorator('group.admin')
268 283 def delete_repos_group_user_perm(self, group_name):
269 284 """
270 285 DELETE an existing repositories group permission user
271 286
272 287 :param group_name:
273 288 """
274 289 try:
290 if not c.rhodecode_user.is_admin:
291 if c.rhodecode_user.user_id == safe_int(request.POST['user_id']):
292 msg = _('Cannot revoke permission for yourself as admin')
293 h.flash(msg, category='warning')
294 raise Exception('revoke admin permission on self')
275 295 recursive = str2bool(request.POST.get('recursive', False))
276 296 ReposGroupModel().delete_permission(
277 297 repos_group=group_name, obj=request.POST['user_id'],
278 298 obj_type='user', recursive=recursive
279 299 )
280 300 Session().commit()
281 301 except Exception:
282 302 log.error(traceback.format_exc())
283 303 h.flash(_('An error occurred during deletion of group user'),
284 304 category='error')
285 305 raise HTTPInternalServerError()
286 306
287 307 @HasReposGroupPermissionAnyDecorator('group.admin')
288 308 def delete_repos_group_users_group_perm(self, group_name):
289 309 """
290 310 DELETE an existing repositories group permission users group
291 311
292 312 :param group_name:
293 313 """
294 314
295 315 try:
296 316 recursive = str2bool(request.POST.get('recursive', False))
297 317 ReposGroupModel().delete_permission(
298 318 repos_group=group_name, obj=request.POST['users_group_id'],
299 319 obj_type='users_group', recursive=recursive
300 320 )
301 321 Session().commit()
302 322 except Exception:
303 323 log.error(traceback.format_exc())
304 324 h.flash(_('An error occurred during deletion of group'
305 325 ' users groups'),
306 326 category='error')
307 327 raise HTTPInternalServerError()
308 328
309 329 def show_by_name(self, group_name):
310 330 """
311 331 This is a proxy that does a lookup group_name -> id, and shows
312 332 the group by id view instead
313 333 """
314 334 group_name = group_name.rstrip('/')
315 335 id_ = RepoGroup.get_by_group_name(group_name)
316 336 if id_:
317 337 return self.show(id_.group_id)
318 338 raise HTTPNotFound
319 339
320 340 @HasReposGroupPermissionAnyDecorator('group.read', 'group.write',
321 341 'group.admin')
322 342 def show(self, group_name, format='html'):
323 343 """GET /repos_groups/group_name: Show a specific item"""
324 344 # url('repos_group', group_name=GROUP_NAME)
325 345
326 346 c.group = c.repos_group = ReposGroupModel()._get_repos_group(group_name)
327 347 c.group_repos = c.group.repositories.all()
328 348
329 349 #overwrite our cached list with current filter
330 350 gr_filter = c.group_repos
331 351 c.repo_cnt = 0
332 352
333 353 groups = RepoGroup.query().order_by(RepoGroup.group_name)\
334 354 .filter(RepoGroup.group_parent_id == c.group.group_id).all()
335 355 c.groups = self.scm_model.get_repos_groups(groups)
336 356
337 357 if c.visual.lightweight_dashboard is False:
338 358 c.repos_list = self.scm_model.get_repos(all_repos=gr_filter)
339 359 ## lightweight version of dashboard
340 360 else:
341 361 c.repos_list = Repository.query()\
342 362 .filter(Repository.group_id == c.group.group_id)\
343 363 .order_by(func.lower(Repository.repo_name))\
344 364 .all()
345 365
346 366 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
347 367 admin=False)
348 368 #json used to render the grid
349 369 c.data = json.dumps(repos_data)
350 370
351 371 return render('admin/repos_groups/repos_groups.html')
352 372
353 373 @HasReposGroupPermissionAnyDecorator('group.admin')
354 374 def edit(self, group_name, format='html'):
355 375 """GET /repos_groups/group_name/edit: Form to edit an existing item"""
356 376 # url('edit_repos_group', group_name=GROUP_NAME)
357 377
358 378 c.repos_group = ReposGroupModel()._get_repos_group(group_name)
359 379 #we can only allow moving empty group if it's already a top-level
360 380 #group, ie has no parents, or we're admin
361 381 if HasPermissionAll('hg.admin')('group edit'):
362 382 #we're global admin, we're ok and we can create TOP level groups
363 383 allow_empty_group = True
364 384 elif not c.repos_group.parent_group:
365 385 allow_empty_group = True
366 386 else:
367 387 allow_empty_group = False
368 388
369 389 self.__load_defaults(allow_empty_group=allow_empty_group,
370 390 exclude_group_ids=[c.repos_group.group_id])
371 391 defaults = self.__load_data(c.repos_group.group_id)
372 392
373 393 return htmlfill.render(
374 394 render('admin/repos_groups/repos_groups_edit.html'),
375 395 defaults=defaults,
376 396 encoding="UTF-8",
377 397 force_defaults=False
378 398 )
@@ -1,120 +1,133 b''
1 1 <table id="permissions_manage" class="noborder">
2 2 <tr>
3 3 <td>${_('none')}</td>
4 4 <td>${_('read')}</td>
5 5 <td>${_('write')}</td>
6 6 <td>${_('admin')}</td>
7 7 <td>${_('member')}</td>
8 8 <td></td>
9 9 </tr>
10 10 ## USERS
11 11 %for r2p in c.repos_group.repo_group_to_perm:
12 ##forbid revoking permission from yourself
12 13 <tr id="id${id(r2p.user.username)}">
14 %if c.rhodecode_user.user_id != r2p.user.user_id or c.rhodecode_user.is_admin:
13 15 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.none')}</td>
14 16 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.read')}</td>
15 17 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.write')}</td>
16 18 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.admin')}</td>
17 19 <td style="white-space: nowrap;">
18 20 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
19 21 </td>
20 22 <td>
21 23 %if r2p.user.username !='default':
22 24 <span class="delete_icon action_button" onclick="ajaxActionUser(${r2p.user.user_id},'${'id%s'%id(r2p.user.username)}')">
23 25 ${_('revoke')}
24 26 </span>
25 27 %endif
26 28 </td>
29 %else:
30 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.none', disabled="disabled")}</td>
31 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.read', disabled="disabled")}</td>
32 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.write', disabled="disabled")}</td>
33 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.admin', disabled="disabled")}</td>
34 <td style="white-space: nowrap;">
35 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
36 </td>
37 <td>
38 </td>
39 %endif
27 40 </tr>
28 41 %endfor
29 42
30 43 ## USERS GROUPS
31 44 %for g2p in c.repos_group.users_group_to_perm:
32 45 <tr id="id${id(g2p.users_group.users_group_name)}">
33 46 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.none')}</td>
34 47 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.read')}</td>
35 48 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.write')}</td>
36 49 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.admin')}</td>
37 50 <td style="white-space: nowrap;">
38 51 <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
39 52 </td>
40 53 <td>
41 54 <span class="delete_icon action_button" onclick="ajaxActionUsersGroup(${g2p.users_group.users_group_id},'${'id%s'%id(g2p.users_group.users_group_name)}')">
42 55 ${_('revoke')}
43 56 </span>
44 57 </td>
45 58 </tr>
46 59 %endfor
47 60 <%
48 61 _tmpl = h.literal("""' \
49 62 <td><input type="radio" value="group.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
50 63 <td><input type="radio" value="group.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
51 64 <td><input type="radio" value="group.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
52 65 <td><input type="radio" value="group.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
53 66 <td class="ac"> \
54 67 <div class="perm_ac" id="perm_ac_{0}"> \
55 68 <input class="yui-ac-input" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text"> \
56 69 <input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden"> \
57 70 <div id="perm_container_{0}"></div> \
58 71 </div> \
59 72 </td> \
60 73 <td></td>'""")
61 74 %>
62 75 ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
63 76 <tr class="new_members last_new_member" id="add_perm_input"></tr>
64 77 <tr>
65 78 <td colspan="6">
66 79 <span id="add_perm" class="add_icon" style="cursor: pointer;">
67 80 ${_('Add another member')}
68 81 </span>
69 82 </td>
70 83 </tr>
71 84 <tr>
72 85 <td colspan="6">
73 86 ${h.checkbox('recursive',value="True", label=_('apply to children'))}
74 87 <span class="help-block">${_('Set or revoke permission to all children of that group, including non-private repositories and other groups')}</span>
75 88 </td>
76 89 </tr>
77 90 </table>
78 91 <script type="text/javascript">
79 92 function ajaxActionUser(user_id, field_id) {
80 93 var sUrl = "${h.url('delete_repos_group_user_perm',group_name=c.repos_group.group_name)}";
81 94 var callback = {
82 95 success: function (o) {
83 96 var tr = YUD.get(String(field_id));
84 97 tr.parentNode.removeChild(tr);
85 98 },
86 99 failure: function (o) {
87 100 alert("${_('Failed to remove user')}");
88 101 },
89 102 };
90 103 var recursive = YUD.get('recursive').checked;
91 104 var postData = '_method=delete&recursive={0}&user_id={1}'.format(recursive,user_id);
92 105 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
93 106 };
94 107
95 108 function ajaxActionUsersGroup(users_group_id,field_id){
96 109 var sUrl = "${h.url('delete_repos_group_users_group_perm',group_name=c.repos_group.group_name)}";
97 110 var callback = {
98 111 success:function(o){
99 112 var tr = YUD.get(String(field_id));
100 113 tr.parentNode.removeChild(tr);
101 114 },
102 115 failure:function(o){
103 116 alert("${_('Failed to remove users group')}");
104 117 },
105 118 };
106 119 var recursive = YUD.get('recursive').checked;
107 120 var postData = '_method=delete&recursive={0}&users_group_id={1}'.format(recursive,users_group_id);
108 121 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
109 122 };
110 123
111 124 YUE.onDOMReady(function () {
112 125 if (!YUD.hasClass('perm_new_member_name', 'error')) {
113 126 YUD.setStyle('add_perm_input', 'display', 'none');
114 127 }
115 128 YAHOO.util.Event.addListener('add_perm', 'click', function () {
116 129 addPermAction(${_tmpl}, ${c.users_array|n}, ${c.users_groups_array|n});
117 130 });
118 131 });
119 132
120 133 </script>
General Comments 0
You need to be logged in to leave comments. Login now