##// END OF EJS Templates
repo-permissions: moved permissions into pyramid....
marcink -
r1734:ddacc559 default
parent child Browse files
Show More
@@ -0,0 +1,98 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
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, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22
23 import deform
24 from pyramid.httpexceptions import HTTPFound
25 from pyramid.view import view_config
26
27 from rhodecode.apps._base import RepoAppView
28 from rhodecode.forms import RcForm
29 from rhodecode.lib import helpers as h
30 from rhodecode.lib import audit_logger
31 from rhodecode.lib.auth import (
32 LoginRequired, HasRepoPermissionAnyDecorator,
33 HasRepoPermissionAllDecorator, CSRFRequired)
34 from rhodecode.model.db import RepositoryField, RepoGroup
35 from rhodecode.model.forms import RepoPermsForm
36 from rhodecode.model.meta import Session
37 from rhodecode.model.repo import RepoModel
38 from rhodecode.model.scm import RepoGroupList, ScmModel
39 from rhodecode.model.validation_schema.schemas import repo_schema
40
41 log = logging.getLogger(__name__)
42
43
44 class RepoSettingsPermissionsView(RepoAppView):
45
46 def load_default_context(self):
47 c = self._get_local_tmpl_context()
48
49 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
50 c.repo_info = self.db_repo
51
52 self._register_global_c(c)
53 return c
54
55 @LoginRequired()
56 @HasRepoPermissionAnyDecorator('repository.admin')
57 @view_config(
58 route_name='edit_repo_perms', request_method='GET',
59 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
60 def edit_permissions(self):
61 c = self.load_default_context()
62 c.active = 'permissions'
63 return self._get_template_context(c)
64
65 @LoginRequired()
66 @HasRepoPermissionAllDecorator('repository.admin')
67 @CSRFRequired()
68 @view_config(
69 route_name='edit_repo_perms', request_method='POST',
70 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
71 def edit_permissions_update(self):
72 _ = self.request.translate
73 c = self.load_default_context()
74 c.active = 'permissions'
75 data = self.request.POST
76 # store private flag outside of HTML to verify if we can modify
77 # default user permissions, prevents submition of FAKE post data
78 # into the form for private repos
79 data['repo_private'] = self.db_repo.private
80 form = RepoPermsForm()().to_python(data)
81 changes = RepoModel().update_permissions(
82 self.db_repo_name, form['perm_additions'], form['perm_updates'],
83 form['perm_deletions'])
84
85 action_data = {
86 'added': changes['added'],
87 'updated': changes['updated'],
88 'deleted': changes['deleted'],
89 }
90 audit_logger.store(
91 'repo.edit.permissions', action_data=action_data,
92 user=self._rhodecode_user, repo=self.db_repo)
93
94 Session().commit()
95 h.flash(_('Repository permissions updated'), category='success')
96
97 raise HTTPFound(
98 self.request.route_path('edit_repo_perms', repo_name=self.db_repo_name))
@@ -31,6 +31,11 b' def includeme(config):'
31 31 name='edit_repo_caches',
32 32 pattern='/{repo_name:.*?[^/]}/settings/caches', repo_route=True)
33 33
34 # Permissions
35 config.add_route(
36 name='edit_repo_perms',
37 pattern='/{repo_name:.*?[^/]}/settings/permissions', repo_route=True)
38
34 39 # Repo Review Rules
35 40 config.add_route(
36 41 name='repo_reviewers',
@@ -39,6 +39,7 b' def route_path(name, params=None, **kwar'
39 39 base_url = {
40 40 'edit_repo': '/{repo_name}/settings',
41 41 'edit_repo_caches': '/{repo_name}/settings/caches',
42 'edit_repo_perms': '/{repo_name}/settings/permissions',
42 43 }[name].format(**kwargs)
43 44
44 45 if params:
@@ -59,7 +60,8 b' def _get_permission_for_user(user, repo)'
59 60 class TestAdminRepoSettings(object):
60 61 @pytest.mark.parametrize('urlname', [
61 62 'edit_repo',
62 'edit_repo_caches'
63 'edit_repo_caches',
64 'edit_repo_perms',
63 65 ])
64 66 def test_show_page(self, urlname, app, backend):
65 67 app.get(route_path(urlname, repo_name=backend.repo_name), status=200)
@@ -72,7 +74,6 b' class TestAdminRepoSettings(object):'
72 74 self.app.get(route_path('edit_repo', repo_name=backend_hg.repo_name))
73 75
74 76 @pytest.mark.parametrize('urlname', [
75 'edit_repo_perms',
76 77 'edit_repo_advanced',
77 78 'repo_vcs_settings',
78 79 'edit_repo_fields',
@@ -661,16 +661,6 b' def make_map(config):'
661 661 requirements=URL_NAME_REQUIREMENTS)
662 662
663 663 # repo edit options
664 rmap.connect('edit_repo_perms', '/{repo_name}/settings/permissions',
665 jsroute=True,
666 controller='admin/repos', action='edit_permissions',
667 conditions={'method': ['GET'], 'function': check_repo},
668 requirements=URL_NAME_REQUIREMENTS)
669 rmap.connect('edit_repo_perms_update', '/{repo_name}/settings/permissions',
670 controller='admin/repos', action='edit_permissions_update',
671 conditions={'method': ['PUT'], 'function': check_repo},
672 requirements=URL_NAME_REQUIREMENTS)
673
674 664 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
675 665 controller='admin/repos', action='edit_fields',
676 666 conditions={'method': ['GET'], 'function': check_repo},
@@ -339,33 +339,6 b' class ReposController(BaseRepoController'
339 339 # url('repo', repo_name=ID)
340 340
341 341 @HasRepoPermissionAllDecorator('repository.admin')
342 def edit_permissions(self, repo_name):
343 """GET /repo_name/settings: Form to edit an existing item"""
344 c.repo_info = self._load_repo(repo_name)
345 c.active = 'permissions'
346 defaults = RepoModel()._get_defaults(repo_name)
347
348 return htmlfill.render(
349 render('admin/repos/repo_edit.mako'),
350 defaults=defaults,
351 encoding="UTF-8",
352 force_defaults=False)
353
354 @HasRepoPermissionAllDecorator('repository.admin')
355 @auth.CSRFRequired()
356 def edit_permissions_update(self, repo_name):
357 form = RepoPermsForm()().to_python(request.POST)
358 RepoModel().update_permissions(repo_name,
359 form['perm_additions'], form['perm_updates'], form['perm_deletions'])
360
361 #TODO: implement this
362 #action_logger(c.rhodecode_user, 'admin_changed_repo_permissions',
363 # repo_name, self.ip_addr, self.sa)
364 Session().commit()
365 h.flash(_('Repository permissions updated'), category='success')
366 return redirect(url('edit_repo_perms', repo_name=repo_name))
367
368 @HasRepoPermissionAllDecorator('repository.admin')
369 342 def edit_fields(self, repo_name):
370 343 """GET /repo_name/settings: Form to edit an existing item"""
371 344 c.repo_info = self._load_repo(repo_name)
@@ -36,6 +36,7 b' ACTIONS = {'
36 36
37 37 'repo.add': {},
38 38 'repo.edit': {},
39 'repo.edit.permissions': {},
39 40 'repo.commit.strip': {}
40 41 }
41 42
@@ -259,7 +259,7 b' class RepoModel(BaseModel):'
259 259 defaults = repo_info.get_dict()
260 260 defaults['repo_name'] = repo_info.just_name
261 261
262 groups = repo_info.groups_with_parents
262 groups = repo_info.groups_with_parents
263 263 parent_group = groups[-1] if groups else None
264 264
265 265 # we use -1 as this is how in HTML, we mark an empty group
@@ -295,16 +295,6 b' class RepoModel(BaseModel):'
295 295 replacement_user = User.get_first_super_admin().username
296 296 defaults.update({'user': replacement_user})
297 297
298 # fill repository users
299 for p in repo_info.repo_to_perm:
300 defaults.update({'u_perm_%s' % p.user.user_id:
301 p.permission.permission_name})
302
303 # fill repository groups
304 for p in repo_info.users_group_to_perm:
305 defaults.update({'g_perm_%s' % p.users_group.users_group_id:
306 p.permission.permission_name})
307
308 298 return defaults
309 299
310 300 def update(self, repo, **kwargs):
@@ -507,10 +497,16 b' class RepoModel(BaseModel):'
507 497
508 498 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
509 499
500 changes = {
501 'added': [],
502 'updated': [],
503 'deleted': []
504 }
510 505 # update permissions
511 506 for member_id, perm, member_type in perm_updates:
512 507 member_id = int(member_id)
513 508 if member_type == 'user':
509 member_name = User.get(member_id).username
514 510 # this updates also current one if found
515 511 self.grant_user_permission(
516 512 repo=repo, user=member_id, perm=perm)
@@ -522,10 +518,14 b' class RepoModel(BaseModel):'
522 518 self.grant_user_group_permission(
523 519 repo=repo, group_name=member_id, perm=perm)
524 520
521 changes['updated'].append({'type': member_type, 'id': member_id,
522 'name': member_name, 'new_perm': perm})
523
525 524 # set new permissions
526 525 for member_id, perm, member_type in perm_additions:
527 526 member_id = int(member_id)
528 527 if member_type == 'user':
528 member_name = User.get(member_id).username
529 529 self.grant_user_permission(
530 530 repo=repo, user=member_id, perm=perm)
531 531 else: # set for user group
@@ -535,11 +535,13 b' class RepoModel(BaseModel):'
535 535 *req_perms)(member_name, user=cur_user):
536 536 self.grant_user_group_permission(
537 537 repo=repo, group_name=member_id, perm=perm)
538
538 changes['added'].append({'type': member_type, 'id': member_id,
539 'name': member_name, 'new_perm': perm})
539 540 # delete permissions
540 541 for member_id, perm, member_type in perm_deletions:
541 542 member_id = int(member_id)
542 543 if member_type == 'user':
544 member_name = User.get(member_id).username
543 545 self.revoke_user_permission(repo=repo, user=member_id)
544 546 else: # set for user group
545 547 # check if we have permissions to alter this usergroup
@@ -549,6 +551,10 b' class RepoModel(BaseModel):'
549 551 self.revoke_user_group_permission(
550 552 repo=repo, group_name=member_id)
551 553
554 changes['deleted'].append({'type': member_type, 'id': member_id,
555 'name': member_name, 'new_perm': perm})
556 return changes
557
552 558 def create_fork(self, form_data, cur_user):
553 559 """
554 560 Simple wrapper into executing celery task for fork creation
@@ -784,7 +784,8 b" def ValidPerms(type_='repo'):"
784 784 del_member = perm_dict.get('id')
785 785 del_type = perm_dict.get('type')
786 786 if del_member and del_type:
787 perm_deletions.add((del_member, None, del_type))
787 perm_deletions.add(
788 (del_member, None, del_type))
788 789
789 790 # store additions in order of how they were added in web form
790 791 for k in sorted(new_perms_group.keys()):
@@ -793,36 +794,48 b" def ValidPerms(type_='repo'):"
793 794 new_type = perm_dict.get('type')
794 795 new_perm = perm_dict.get('perm')
795 796 if new_member and new_perm and new_type:
796 perm_additions.add((new_member, new_perm, new_type))
797 perm_additions.add(
798 (new_member, new_perm, new_type))
797 799
798 800 # get updates of permissions
799 801 # (read the existing radio button states)
802 default_user_id = User.get_default_user().user_id
800 803 for k, update_value in value.iteritems():
801 804 if k.startswith('u_perm_') or k.startswith('g_perm_'):
802 805 member = k[7:]
803 806 update_type = {'u': 'user',
804 807 'g': 'users_group'}[k[0]]
805 if member == User.DEFAULT_USER:
808
809 if safe_int(member) == default_user_id:
806 810 if str2bool(value.get('repo_private')):
807 # set none for default when updating to
808 # private repo protects agains form manipulation
811 # prevent from updating default user permissions
812 # when this repository is marked as private
809 813 update_value = EMPTY_PERM
810 perm_updates.add((member, update_value, update_type))
811 # check the deletes
812 814
813 value['perm_additions'] = list(perm_additions)
815 perm_updates.add(
816 (member, update_value, update_type))
817
818 value['perm_additions'] = [] # propagated later
814 819 value['perm_updates'] = list(perm_updates)
815 820 value['perm_deletions'] = list(perm_deletions)
816 821
817 # validate users they exist and they are active !
818 for member_id, _perm, member_type in perm_additions:
822 updates_map = dict(
823 (x[0], (x[1], x[2])) for x in value['perm_updates'])
824 # make sure Additions don't override updates.
825 for member_id, perm, member_type in list(perm_additions):
826 if member_id in updates_map:
827 perm = updates_map[member_id][0]
828 value['perm_additions'].append((member_id, perm, member_type))
829
830 # on new entries validate users they exist and they are active !
831 # this leaves feedback to the form
819 832 try:
820 833 if member_type == 'user':
821 self.user_db = User.query()\
834 User.query()\
822 835 .filter(User.active == true())\
823 836 .filter(User.user_id == member_id).one()
824 837 if member_type == 'users_group':
825 self.user_db = UserGroup.query()\
838 UserGroup.query()\
826 839 .filter(UserGroup.users_group_active == true())\
827 840 .filter(UserGroup.users_group_id == member_id)\
828 841 .one()
@@ -24,7 +24,6 b' function registerRCRoutes() {'
24 24 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
25 25 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/default-reviewers', ['repo_name']);
26 26 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
27 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
28 27 pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']);
29 28 pyroutes.register('changeset_comment_preview', '/%(repo_name)s/changeset/comment/preview', ['repo_name']);
30 29 pyroutes.register('changeset_comment_delete', '/%(repo_name)s/changeset/comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
@@ -99,6 +98,7 b' function registerRCRoutes() {'
99 98 pyroutes.register('goto_switcher_data', '/_goto_data', []);
100 99 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
101 100 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
101 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
102 102 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
103 103 pyroutes.register('repo_maintenance', '/%(repo_name)s/maintenance', ['repo_name']);
104 104 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/maintenance/execute', ['repo_name']);
@@ -42,7 +42,7 b''
42 42 <a href="${h.route_path('edit_repo', repo_name=c.repo_name)}">${_('Settings')}</a>
43 43 </li>
44 44 <li class="${'active' if c.active=='permissions' else ''}">
45 <a href="${h.url('edit_repo_perms', repo_name=c.repo_name)}">${_('Permissions')}</a>
45 <a href="${h.route_path('edit_repo_perms', repo_name=c.repo_name)}">${_('Permissions')}</a>
46 46 </li>
47 47 <li class="${'active' if c.active=='advanced' else ''}">
48 48 <a href="${h.url('edit_repo_advanced', repo_name=c.repo_name)}">${_('Advanced')}</a>
@@ -5,8 +5,7 b''
5 5 <h3 class="panel-title">${_('Repository Permissions')}</h3>
6 6 </div>
7 7 <div class="panel-body">
8 ${h.secure_form(url('edit_repo_perms_update', repo_name=c.repo_name), method='put')}
9 ${h.hidden('repo_private')}
8 ${h.secure_form(h.route_path('edit_repo_perms', repo_name=c.repo_name), method='POST')}
10 9 <table id="permissions_manage" class="rctable permissions">
11 10 <tr>
12 11 <th class="td-radio">${_('None')}</th>
@@ -40,7 +39,7 b''
40 39 <tr>
41 40 <td colspan="4">
42 41 <span class="private_repo_msg">
43 <strong>${_('private repository')}</strong>
42 <strong title="${_user.permission}">${_('private repository')}</strong>
44 43 </span>
45 44 </td>
46 45 <td class="private_repo_msg">
@@ -50,10 +49,10 b''
50 49 </tr>
51 50 %else:
52 51 <tr>
53 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'repository.none')}</td>
54 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'repository.read')}</td>
55 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'repository.write')}</td>
56 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'repository.admin')}</td>
52 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'repository.none', checked=_user.permission=='repository.none')}</td>
53 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'repository.read', checked=_user.permission=='repository.read')}</td>
54 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'repository.write', checked=_user.permission=='repository.write')}</td>
55 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'repository.admin', checked=_user.permission=='repository.admin')}</td>
57 56 <td class="td-user">
58 57 ${base.gravatar(_user.email, 16)}
59 58 <span class="user">
@@ -79,10 +78,10 b''
79 78 ## USER GROUPS
80 79 %for _user_group in c.repo_info.permission_user_groups():
81 80 <tr>
82 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'repository.none')}</td>
83 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'repository.read')}</td>
84 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'repository.write')}</td>
85 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'repository.admin')}</td>
81 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'repository.none', checked=_user_group.permission=='repository.none')}</td>
82 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'repository.read', checked=_user_group.permission=='repository.read')}</td>
83 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'repository.write', checked=_user_group.permission=='repository.write')}</td>
84 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'repository.admin', checked=_user_group.permission=='repository.admin')}</td>
86 85 <td class="td-componentname">
87 86 <i class="icon-group" ></i>
88 87 %if h.HasPermissionAny('hg.admin')():
@@ -146,7 +146,7 b''
146 146 %if actions:
147 147 <td class="td-action">
148 148 %if section == 'repositories':
149 <a href="${h.url('edit_repo_perms',repo_name=k,anchor='permissions_manage')}">${_('edit')}</a>
149 <a href="${h.route_path('edit_repo_perms',repo_name=k,anchor='permissions_manage')}">${_('edit')}</a>
150 150 %elif section == 'repositories_groups':
151 151 <a href="${h.url('edit_repo_group_perms',group_name=k,anchor='permissions_manage')}">${_('edit')}</a>
152 152 %elif section == 'user_groups':
General Comments 0
You need to be logged in to leave comments. Login now