##// END OF EJS Templates
permissions: handle more cases for invalidating permission caches...
marcink -
r3383:c5723c68 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,207 +1,215 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import formencode
23 23 import formencode.htmlfill
24 24
25 25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
26 26 from pyramid.view import view_config
27 27 from pyramid.renderers import render
28 28 from pyramid.response import Response
29 29
30 30 from rhodecode import events
31 31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 32
33 33 from rhodecode.lib.ext_json import json
34 34 from rhodecode.lib.auth import (
35 35 LoginRequired, CSRFRequired, NotAnonymous,
36 36 HasPermissionAny, HasRepoGroupPermissionAny)
37 37 from rhodecode.lib import helpers as h, audit_logger
38 38 from rhodecode.lib.utils2 import safe_int, safe_unicode
39 39 from rhodecode.model.forms import RepoGroupForm
40 40 from rhodecode.model.repo_group import RepoGroupModel
41 41 from rhodecode.model.scm import RepoGroupList
42 42 from rhodecode.model.db import Session, RepoGroup
43 43
44 44 log = logging.getLogger(__name__)
45 45
46 46
47 47 class AdminRepoGroupsView(BaseAppView, DataGridAppView):
48 48
49 49 def load_default_context(self):
50 50 c = self._get_local_tmpl_context()
51 51
52 52 return c
53 53
54 54 def _load_form_data(self, c):
55 55 allow_empty_group = False
56 56
57 57 if self._can_create_repo_group():
58 58 # we're global admin, we're ok and we can create TOP level groups
59 59 allow_empty_group = True
60 60
61 61 # override the choices for this form, we need to filter choices
62 62 # and display only those we have ADMIN right
63 63 groups_with_admin_rights = RepoGroupList(
64 64 RepoGroup.query().all(),
65 65 perm_set=['group.admin'])
66 66 c.repo_groups = RepoGroup.groups_choices(
67 67 groups=groups_with_admin_rights,
68 68 show_empty_group=allow_empty_group)
69 69
70 70 def _can_create_repo_group(self, parent_group_id=None):
71 71 is_admin = HasPermissionAny('hg.admin')('group create controller')
72 72 create_repo_group = HasPermissionAny(
73 73 'hg.repogroup.create.true')('group create controller')
74 74 if is_admin or (create_repo_group and not parent_group_id):
75 75 # we're global admin, or we have global repo group create
76 76 # permission
77 77 # we're ok and we can create TOP level groups
78 78 return True
79 79 elif parent_group_id:
80 80 # we check the permission if we can write to parent group
81 81 group = RepoGroup.get(parent_group_id)
82 82 group_name = group.group_name if group else None
83 83 if HasRepoGroupPermissionAny('group.admin')(
84 84 group_name, 'check if user is an admin of group'):
85 85 # we're an admin of passed in group, we're ok.
86 86 return True
87 87 else:
88 88 return False
89 89 return False
90 90
91 91 @LoginRequired()
92 92 @NotAnonymous()
93 93 # perms check inside
94 94 @view_config(
95 95 route_name='repo_groups', request_method='GET',
96 96 renderer='rhodecode:templates/admin/repo_groups/repo_groups.mako')
97 97 def repo_group_list(self):
98 98 c = self.load_default_context()
99 99
100 100 repo_group_list = RepoGroup.get_all_repo_groups()
101 101 repo_group_list_acl = RepoGroupList(
102 102 repo_group_list, perm_set=['group.admin'])
103 103 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
104 104 repo_group_list=repo_group_list_acl, admin=True)
105 105 c.data = json.dumps(repo_group_data)
106 106 return self._get_template_context(c)
107 107
108 108 @LoginRequired()
109 109 @NotAnonymous()
110 110 # perm checks inside
111 111 @view_config(
112 112 route_name='repo_group_new', request_method='GET',
113 113 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
114 114 def repo_group_new(self):
115 115 c = self.load_default_context()
116 116
117 117 # perm check for admin, create_group perm or admin of parent_group
118 118 parent_group_id = safe_int(self.request.GET.get('parent_group'))
119 119 if not self._can_create_repo_group(parent_group_id):
120 120 raise HTTPForbidden()
121 121
122 122 self._load_form_data(c)
123 123
124 124 defaults = {} # Future proof for default of repo group
125 125 data = render(
126 126 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
127 127 self._get_template_context(c), self.request)
128 128 html = formencode.htmlfill.render(
129 129 data,
130 130 defaults=defaults,
131 131 encoding="UTF-8",
132 132 force_defaults=False
133 133 )
134 134 return Response(html)
135 135
136 136 @LoginRequired()
137 137 @NotAnonymous()
138 138 @CSRFRequired()
139 139 # perm checks inside
140 140 @view_config(
141 141 route_name='repo_group_create', request_method='POST',
142 142 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
143 143 def repo_group_create(self):
144 144 c = self.load_default_context()
145 145 _ = self.request.translate
146 146
147 147 parent_group_id = safe_int(self.request.POST.get('group_parent_id'))
148 148 can_create = self._can_create_repo_group(parent_group_id)
149 149
150 150 self._load_form_data(c)
151 151 # permissions for can create group based on parent_id are checked
152 152 # here in the Form
153 153 available_groups = map(lambda k: safe_unicode(k[0]), c.repo_groups)
154 154 repo_group_form = RepoGroupForm(
155 155 self.request.translate, available_groups=available_groups,
156 156 can_create_in_root=can_create)()
157 157
158 158 repo_group_name = self.request.POST.get('group_name')
159 159 try:
160 160 owner = self._rhodecode_user
161 161 form_result = repo_group_form.to_python(dict(self.request.POST))
162 copy_permissions = form_result.get('group_copy_permissions')
162 163 repo_group = RepoGroupModel().create(
163 164 group_name=form_result['group_name_full'],
164 165 group_description=form_result['group_description'],
165 166 owner=owner.user_id,
166 167 copy_permissions=form_result['group_copy_permissions']
167 168 )
168 169 Session().flush()
169 170
170 171 repo_group_data = repo_group.get_api_data()
171 172 audit_logger.store_web(
172 173 'repo_group.create', action_data={'data': repo_group_data},
173 174 user=self._rhodecode_user)
174 175
175 176 Session().commit()
176 177
177 178 _new_group_name = form_result['group_name_full']
178 179
179 180 repo_group_url = h.link_to(
180 181 _new_group_name,
181 182 h.route_path('repo_group_home', repo_group_name=_new_group_name))
182 183 h.flash(h.literal(_('Created repository group %s')
183 184 % repo_group_url), category='success')
184 185
185 186 except formencode.Invalid as errors:
186 187 data = render(
187 188 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
188 189 self._get_template_context(c), self.request)
189 190 html = formencode.htmlfill.render(
190 191 data,
191 192 defaults=errors.value,
192 193 errors=errors.error_dict or {},
193 194 prefix_error=False,
194 195 encoding="UTF-8",
195 196 force_defaults=False
196 197 )
197 198 return Response(html)
198 199 except Exception:
199 200 log.exception("Exception during creation of repository group")
200 201 h.flash(_('Error occurred during creation of repository group %s')
201 202 % repo_group_name, category='error')
202 203 raise HTTPFound(h.route_path('home'))
203 204
204 events.trigger(events.UserPermissionsChange([self._rhodecode_user.user_id]))
205 affected_user_ids = [self._rhodecode_user.user_id]
206 if copy_permissions:
207 user_group_perms = repo_group.permissions(expand_from_user_groups=True)
208 copy_perms = [perm['user_id'] for perm in user_group_perms]
209 # also include those newly created by copy
210 affected_user_ids.extend(copy_perms)
211 events.trigger(events.UserPermissionsChange(affected_user_ids))
212
205 213 raise HTTPFound(
206 214 h.route_path('repo_group_home',
207 215 repo_group_name=form_result['group_name_full']))
@@ -1,184 +1,194 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import formencode
23 23 import formencode.htmlfill
24 24
25 25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
26 26 from pyramid.view import view_config
27 27 from pyramid.renderers import render
28 28 from pyramid.response import Response
29 29
30 30 from rhodecode import events
31 31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 32 from rhodecode.lib.celerylib.utils import get_task_id
33 33
34 34 from rhodecode.lib.ext_json import json
35 35 from rhodecode.lib.auth import (
36 36 LoginRequired, CSRFRequired, NotAnonymous,
37 37 HasPermissionAny, HasRepoGroupPermissionAny)
38 38 from rhodecode.lib import helpers as h
39 39 from rhodecode.lib.utils import repo_name_slug
40 40 from rhodecode.lib.utils2 import safe_int, safe_unicode
41 41 from rhodecode.model.forms import RepoForm
42 42 from rhodecode.model.repo import RepoModel
43 43 from rhodecode.model.scm import RepoList, RepoGroupList, ScmModel
44 44 from rhodecode.model.settings import SettingsModel
45 45 from rhodecode.model.db import Repository, RepoGroup
46 46
47 47 log = logging.getLogger(__name__)
48 48
49 49
50 50 class AdminReposView(BaseAppView, DataGridAppView):
51 51
52 52 def load_default_context(self):
53 53 c = self._get_local_tmpl_context()
54 54
55 55 return c
56 56
57 57 def _load_form_data(self, c):
58 58 acl_groups = RepoGroupList(RepoGroup.query().all(),
59 59 perm_set=['group.write', 'group.admin'])
60 60 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
61 61 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
62 62 c.landing_revs_choices, c.landing_revs = \
63 63 ScmModel().get_repo_landing_revs(self.request.translate)
64 64 c.personal_repo_group = self._rhodecode_user.personal_repo_group
65 65
66 66 @LoginRequired()
67 67 @NotAnonymous()
68 68 # perms check inside
69 69 @view_config(
70 70 route_name='repos', request_method='GET',
71 71 renderer='rhodecode:templates/admin/repos/repos.mako')
72 72 def repository_list(self):
73 73 c = self.load_default_context()
74 74
75 75 repo_list = Repository.get_all_repos()
76 76 c.repo_list = RepoList(repo_list, perm_set=['repository.admin'])
77 77 repos_data = RepoModel().get_repos_as_dict(
78 78 repo_list=c.repo_list, admin=True, super_user_actions=True)
79 79 # json used to render the grid
80 80 c.data = json.dumps(repos_data)
81 81
82 82 return self._get_template_context(c)
83 83
84 84 @LoginRequired()
85 85 @NotAnonymous()
86 86 # perms check inside
87 87 @view_config(
88 88 route_name='repo_new', request_method='GET',
89 89 renderer='rhodecode:templates/admin/repos/repo_add.mako')
90 90 def repository_new(self):
91 91 c = self.load_default_context()
92 92
93 93 new_repo = self.request.GET.get('repo', '')
94 94 parent_group = safe_int(self.request.GET.get('parent_group'))
95 95 _gr = RepoGroup.get(parent_group)
96 96
97 97 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
98 98 # you're not super admin nor have global create permissions,
99 99 # but maybe you have at least write permission to a parent group ?
100 100
101 101 gr_name = _gr.group_name if _gr else None
102 102 # create repositories with write permission on group is set to true
103 103 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
104 104 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
105 105 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
106 106 if not (group_admin or (group_write and create_on_write)):
107 107 raise HTTPForbidden()
108 108
109 109 self._load_form_data(c)
110 110 c.new_repo = repo_name_slug(new_repo)
111 111
112 112 # apply the defaults from defaults page
113 113 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
114 114 # set checkbox to autochecked
115 115 defaults['repo_copy_permissions'] = True
116 116
117 117 parent_group_choice = '-1'
118 118 if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group:
119 119 parent_group_choice = self._rhodecode_user.personal_repo_group
120 120
121 121 if parent_group and _gr:
122 122 if parent_group in [x[0] for x in c.repo_groups]:
123 123 parent_group_choice = safe_unicode(parent_group)
124 124
125 125 defaults.update({'repo_group': parent_group_choice})
126 126
127 127 data = render('rhodecode:templates/admin/repos/repo_add.mako',
128 128 self._get_template_context(c), self.request)
129 129 html = formencode.htmlfill.render(
130 130 data,
131 131 defaults=defaults,
132 132 encoding="UTF-8",
133 133 force_defaults=False
134 134 )
135 135 return Response(html)
136 136
137 137 @LoginRequired()
138 138 @NotAnonymous()
139 139 @CSRFRequired()
140 140 # perms check inside
141 141 @view_config(
142 142 route_name='repo_create', request_method='POST',
143 143 renderer='rhodecode:templates/admin/repos/repos.mako')
144 144 def repository_create(self):
145 145 c = self.load_default_context()
146 146
147 147 form_result = {}
148 148 self._load_form_data(c)
149 task_id = None
149
150 150 try:
151 151 # CanWriteToGroup validators checks permissions of this POST
152 152 form = RepoForm(
153 153 self.request.translate, repo_groups=c.repo_groups_choices,
154 154 landing_revs=c.landing_revs_choices)()
155 155 form_result = form.to_python(dict(self.request.POST))
156
156 copy_permissions = form_result.get('repo_copy_permissions')
157 157 # create is done sometimes async on celery, db transaction
158 158 # management is handled there.
159 159 task = RepoModel().create(form_result, self._rhodecode_user.user_id)
160 160 task_id = get_task_id(task)
161 161 except formencode.Invalid as errors:
162 162 data = render('rhodecode:templates/admin/repos/repo_add.mako',
163 163 self._get_template_context(c), self.request)
164 164 html = formencode.htmlfill.render(
165 165 data,
166 166 defaults=errors.value,
167 167 errors=errors.error_dict or {},
168 168 prefix_error=False,
169 169 encoding="UTF-8",
170 170 force_defaults=False
171 171 )
172 172 return Response(html)
173 173
174 174 except Exception as e:
175 175 msg = self._log_creation_exception(e, form_result.get('repo_name'))
176 176 h.flash(msg, category='error')
177 177 raise HTTPFound(h.route_path('home'))
178 178
179 events.trigger(events.UserPermissionsChange([self._rhodecode_user.user_id]))
179 repo_name = form_result.get('repo_name_full')
180
181 affected_user_ids = [self._rhodecode_user.user_id]
182 if copy_permissions:
183 repository = Repository.get_by_repo_name(repo_name)
184 # also include those newly created by copy
185 user_group_perms = repository.permissions(expand_from_user_groups=True)
186 copy_perms = [perm['user_id'] for perm in user_group_perms]
187 # also include those newly created by copy
188 affected_user_ids.extend(copy_perms)
189
190 events.trigger(events.UserPermissionsChange(affected_user_ids))
180 191
181 192 raise HTTPFound(
182 h.route_path('repo_creating',
183 repo_name=form_result['repo_name_full'],
193 h.route_path('repo_creating', repo_name=repo_name,
184 194 _query=dict(task_id=task_id)))
@@ -1,1261 +1,1266 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import datetime
23 23 import formencode
24 24 import formencode.htmlfill
25 25
26 26 from pyramid.httpexceptions import HTTPFound
27 27 from pyramid.view import view_config
28 28 from pyramid.renderers import render
29 29 from pyramid.response import Response
30 30
31 from rhodecode import events
31 32 from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView
32 33 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
33 34 from rhodecode.authentication.plugins import auth_rhodecode
34 35 from rhodecode.events import trigger
35 36 from rhodecode.model.db import true
36 37
37 38 from rhodecode.lib import audit_logger, rc_cache
38 39 from rhodecode.lib.exceptions import (
39 40 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
40 41 UserOwnsUserGroupsException, DefaultUserException)
41 42 from rhodecode.lib.ext_json import json
42 43 from rhodecode.lib.auth import (
43 44 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
44 45 from rhodecode.lib import helpers as h
45 46 from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict
46 47 from rhodecode.model.auth_token import AuthTokenModel
47 48 from rhodecode.model.forms import (
48 49 UserForm, UserIndividualPermissionsForm, UserPermissionsForm,
49 50 UserExtraEmailForm, UserExtraIpForm)
50 51 from rhodecode.model.permission import PermissionModel
51 52 from rhodecode.model.repo_group import RepoGroupModel
52 53 from rhodecode.model.ssh_key import SshKeyModel
53 54 from rhodecode.model.user import UserModel
54 55 from rhodecode.model.user_group import UserGroupModel
55 56 from rhodecode.model.db import (
56 57 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
57 58 UserApiKeys, UserSshKeys, RepoGroup)
58 59 from rhodecode.model.meta import Session
59 60
60 61 log = logging.getLogger(__name__)
61 62
62 63
63 64 class AdminUsersView(BaseAppView, DataGridAppView):
64 65
65 66 def load_default_context(self):
66 67 c = self._get_local_tmpl_context()
67 68 return c
68 69
69 70 @LoginRequired()
70 71 @HasPermissionAllDecorator('hg.admin')
71 72 @view_config(
72 73 route_name='users', request_method='GET',
73 74 renderer='rhodecode:templates/admin/users/users.mako')
74 75 def users_list(self):
75 76 c = self.load_default_context()
76 77 return self._get_template_context(c)
77 78
78 79 @LoginRequired()
79 80 @HasPermissionAllDecorator('hg.admin')
80 81 @view_config(
81 82 # renderer defined below
82 83 route_name='users_data', request_method='GET',
83 84 renderer='json_ext', xhr=True)
84 85 def users_list_data(self):
85 86 self.load_default_context()
86 87 column_map = {
87 88 'first_name': 'name',
88 89 'last_name': 'lastname',
89 90 }
90 91 draw, start, limit = self._extract_chunk(self.request)
91 92 search_q, order_by, order_dir = self._extract_ordering(
92 93 self.request, column_map=column_map)
93 94 _render = self.request.get_partial_renderer(
94 95 'rhodecode:templates/data_table/_dt_elements.mako')
95 96
96 97 def user_actions(user_id, username):
97 98 return _render("user_actions", user_id, username)
98 99
99 100 users_data_total_count = User.query()\
100 101 .filter(User.username != User.DEFAULT_USER) \
101 102 .count()
102 103
103 104 users_data_total_inactive_count = User.query()\
104 105 .filter(User.username != User.DEFAULT_USER) \
105 106 .filter(User.active != true())\
106 107 .count()
107 108
108 109 # json generate
109 110 base_q = User.query().filter(User.username != User.DEFAULT_USER)
110 111 base_inactive_q = base_q.filter(User.active != true())
111 112
112 113 if search_q:
113 114 like_expression = u'%{}%'.format(safe_unicode(search_q))
114 115 base_q = base_q.filter(or_(
115 116 User.username.ilike(like_expression),
116 117 User._email.ilike(like_expression),
117 118 User.name.ilike(like_expression),
118 119 User.lastname.ilike(like_expression),
119 120 ))
120 121 base_inactive_q = base_q.filter(User.active != true())
121 122
122 123 users_data_total_filtered_count = base_q.count()
123 124 users_data_total_filtered_inactive_count = base_inactive_q.count()
124 125
125 126 sort_col = getattr(User, order_by, None)
126 127 if sort_col:
127 128 if order_dir == 'asc':
128 129 # handle null values properly to order by NULL last
129 130 if order_by in ['last_activity']:
130 131 sort_col = coalesce(sort_col, datetime.date.max)
131 132 sort_col = sort_col.asc()
132 133 else:
133 134 # handle null values properly to order by NULL last
134 135 if order_by in ['last_activity']:
135 136 sort_col = coalesce(sort_col, datetime.date.min)
136 137 sort_col = sort_col.desc()
137 138
138 139 base_q = base_q.order_by(sort_col)
139 140 base_q = base_q.offset(start).limit(limit)
140 141
141 142 users_list = base_q.all()
142 143
143 144 users_data = []
144 145 for user in users_list:
145 146 users_data.append({
146 147 "username": h.gravatar_with_user(self.request, user.username),
147 148 "email": user.email,
148 149 "first_name": user.first_name,
149 150 "last_name": user.last_name,
150 151 "last_login": h.format_date(user.last_login),
151 152 "last_activity": h.format_date(user.last_activity),
152 153 "active": h.bool2icon(user.active),
153 154 "active_raw": user.active,
154 155 "admin": h.bool2icon(user.admin),
155 156 "extern_type": user.extern_type,
156 157 "extern_name": user.extern_name,
157 158 "action": user_actions(user.user_id, user.username),
158 159 })
159 160 data = ({
160 161 'draw': draw,
161 162 'data': users_data,
162 163 'recordsTotal': users_data_total_count,
163 164 'recordsFiltered': users_data_total_filtered_count,
164 165 'recordsTotalInactive': users_data_total_inactive_count,
165 166 'recordsFilteredInactive': users_data_total_filtered_inactive_count
166 167 })
167 168
168 169 return data
169 170
170 171 def _set_personal_repo_group_template_vars(self, c_obj):
171 172 DummyUser = AttributeDict({
172 173 'username': '${username}',
173 174 'user_id': '${user_id}',
174 175 })
175 176 c_obj.default_create_repo_group = RepoGroupModel() \
176 177 .get_default_create_personal_repo_group()
177 178 c_obj.personal_repo_group_name = RepoGroupModel() \
178 179 .get_personal_group_name(DummyUser)
179 180
180 181 @LoginRequired()
181 182 @HasPermissionAllDecorator('hg.admin')
182 183 @view_config(
183 184 route_name='users_new', request_method='GET',
184 185 renderer='rhodecode:templates/admin/users/user_add.mako')
185 186 def users_new(self):
186 187 _ = self.request.translate
187 188 c = self.load_default_context()
188 189 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
189 190 self._set_personal_repo_group_template_vars(c)
190 191 return self._get_template_context(c)
191 192
192 193 @LoginRequired()
193 194 @HasPermissionAllDecorator('hg.admin')
194 195 @CSRFRequired()
195 196 @view_config(
196 197 route_name='users_create', request_method='POST',
197 198 renderer='rhodecode:templates/admin/users/user_add.mako')
198 199 def users_create(self):
199 200 _ = self.request.translate
200 201 c = self.load_default_context()
201 202 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
202 203 user_model = UserModel()
203 204 user_form = UserForm(self.request.translate)()
204 205 try:
205 206 form_result = user_form.to_python(dict(self.request.POST))
206 207 user = user_model.create(form_result)
207 208 Session().flush()
208 209 creation_data = user.get_api_data()
209 210 username = form_result['username']
210 211
211 212 audit_logger.store_web(
212 213 'user.create', action_data={'data': creation_data},
213 214 user=c.rhodecode_user)
214 215
215 216 user_link = h.link_to(
216 217 h.escape(username),
217 218 h.route_path('user_edit', user_id=user.user_id))
218 219 h.flash(h.literal(_('Created user %(user_link)s')
219 220 % {'user_link': user_link}), category='success')
220 221 Session().commit()
221 222 except formencode.Invalid as errors:
222 223 self._set_personal_repo_group_template_vars(c)
223 224 data = render(
224 225 'rhodecode:templates/admin/users/user_add.mako',
225 226 self._get_template_context(c), self.request)
226 227 html = formencode.htmlfill.render(
227 228 data,
228 229 defaults=errors.value,
229 230 errors=errors.error_dict or {},
230 231 prefix_error=False,
231 232 encoding="UTF-8",
232 233 force_defaults=False
233 234 )
234 235 return Response(html)
235 236 except UserCreationError as e:
236 237 h.flash(e, 'error')
237 238 except Exception:
238 239 log.exception("Exception creation of user")
239 240 h.flash(_('Error occurred during creation of user %s')
240 241 % self.request.POST.get('username'), category='error')
241 242 raise HTTPFound(h.route_path('users'))
242 243
243 244
244 245 class UsersView(UserAppView):
245 246 ALLOW_SCOPED_TOKENS = False
246 247 """
247 248 This view has alternative version inside EE, if modified please take a look
248 249 in there as well.
249 250 """
250 251
251 252 def load_default_context(self):
252 253 c = self._get_local_tmpl_context()
253 254 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
254 255 c.allowed_languages = [
255 256 ('en', 'English (en)'),
256 257 ('de', 'German (de)'),
257 258 ('fr', 'French (fr)'),
258 259 ('it', 'Italian (it)'),
259 260 ('ja', 'Japanese (ja)'),
260 261 ('pl', 'Polish (pl)'),
261 262 ('pt', 'Portuguese (pt)'),
262 263 ('ru', 'Russian (ru)'),
263 264 ('zh', 'Chinese (zh)'),
264 265 ]
265 266 req = self.request
266 267
267 268 c.available_permissions = req.registry.settings['available_permissions']
268 269 PermissionModel().set_global_permission_choices(
269 270 c, gettext_translator=req.translate)
270 271
271 272 return c
272 273
273 274 @LoginRequired()
274 275 @HasPermissionAllDecorator('hg.admin')
275 276 @CSRFRequired()
276 277 @view_config(
277 278 route_name='user_update', request_method='POST',
278 279 renderer='rhodecode:templates/admin/users/user_edit.mako')
279 280 def user_update(self):
280 281 _ = self.request.translate
281 282 c = self.load_default_context()
282 283
283 284 user_id = self.db_user_id
284 285 c.user = self.db_user
285 286
286 287 c.active = 'profile'
287 288 c.extern_type = c.user.extern_type
288 289 c.extern_name = c.user.extern_name
289 290 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
290 291 available_languages = [x[0] for x in c.allowed_languages]
291 292 _form = UserForm(self.request.translate, edit=True,
292 293 available_languages=available_languages,
293 294 old_data={'user_id': user_id,
294 295 'email': c.user.email})()
295 296 form_result = {}
296 297 old_values = c.user.get_api_data()
297 298 try:
298 299 form_result = _form.to_python(dict(self.request.POST))
299 300 skip_attrs = ['extern_type', 'extern_name']
300 301 # TODO: plugin should define if username can be updated
301 302 if c.extern_type != "rhodecode":
302 303 # forbid updating username for external accounts
303 304 skip_attrs.append('username')
304 305
305 306 UserModel().update_user(
306 307 user_id, skip_attrs=skip_attrs, **form_result)
307 308
308 309 audit_logger.store_web(
309 310 'user.edit', action_data={'old_data': old_values},
310 311 user=c.rhodecode_user)
311 312
312 313 Session().commit()
313 314 h.flash(_('User updated successfully'), category='success')
314 315 except formencode.Invalid as errors:
315 316 data = render(
316 317 'rhodecode:templates/admin/users/user_edit.mako',
317 318 self._get_template_context(c), self.request)
318 319 html = formencode.htmlfill.render(
319 320 data,
320 321 defaults=errors.value,
321 322 errors=errors.error_dict or {},
322 323 prefix_error=False,
323 324 encoding="UTF-8",
324 325 force_defaults=False
325 326 )
326 327 return Response(html)
327 328 except UserCreationError as e:
328 329 h.flash(e, 'error')
329 330 except Exception:
330 331 log.exception("Exception updating user")
331 332 h.flash(_('Error occurred during update of user %s')
332 333 % form_result.get('username'), category='error')
333 334 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
334 335
335 336 @LoginRequired()
336 337 @HasPermissionAllDecorator('hg.admin')
337 338 @CSRFRequired()
338 339 @view_config(
339 340 route_name='user_delete', request_method='POST',
340 341 renderer='rhodecode:templates/admin/users/user_edit.mako')
341 342 def user_delete(self):
342 343 _ = self.request.translate
343 344 c = self.load_default_context()
344 345 c.user = self.db_user
345 346
346 347 _repos = c.user.repositories
347 348 _repo_groups = c.user.repository_groups
348 349 _user_groups = c.user.user_groups
349 350
350 351 handle_repos = None
351 352 handle_repo_groups = None
352 353 handle_user_groups = None
353 354 # dummy call for flash of handle
354 355 set_handle_flash_repos = lambda: None
355 356 set_handle_flash_repo_groups = lambda: None
356 357 set_handle_flash_user_groups = lambda: None
357 358
358 359 if _repos and self.request.POST.get('user_repos'):
359 360 do = self.request.POST['user_repos']
360 361 if do == 'detach':
361 362 handle_repos = 'detach'
362 363 set_handle_flash_repos = lambda: h.flash(
363 364 _('Detached %s repositories') % len(_repos),
364 365 category='success')
365 366 elif do == 'delete':
366 367 handle_repos = 'delete'
367 368 set_handle_flash_repos = lambda: h.flash(
368 369 _('Deleted %s repositories') % len(_repos),
369 370 category='success')
370 371
371 372 if _repo_groups and self.request.POST.get('user_repo_groups'):
372 373 do = self.request.POST['user_repo_groups']
373 374 if do == 'detach':
374 375 handle_repo_groups = 'detach'
375 376 set_handle_flash_repo_groups = lambda: h.flash(
376 377 _('Detached %s repository groups') % len(_repo_groups),
377 378 category='success')
378 379 elif do == 'delete':
379 380 handle_repo_groups = 'delete'
380 381 set_handle_flash_repo_groups = lambda: h.flash(
381 382 _('Deleted %s repository groups') % len(_repo_groups),
382 383 category='success')
383 384
384 385 if _user_groups and self.request.POST.get('user_user_groups'):
385 386 do = self.request.POST['user_user_groups']
386 387 if do == 'detach':
387 388 handle_user_groups = 'detach'
388 389 set_handle_flash_user_groups = lambda: h.flash(
389 390 _('Detached %s user groups') % len(_user_groups),
390 391 category='success')
391 392 elif do == 'delete':
392 393 handle_user_groups = 'delete'
393 394 set_handle_flash_user_groups = lambda: h.flash(
394 395 _('Deleted %s user groups') % len(_user_groups),
395 396 category='success')
396 397
397 398 old_values = c.user.get_api_data()
398 399 try:
399 400 UserModel().delete(c.user, handle_repos=handle_repos,
400 401 handle_repo_groups=handle_repo_groups,
401 402 handle_user_groups=handle_user_groups)
402 403
403 404 audit_logger.store_web(
404 405 'user.delete', action_data={'old_data': old_values},
405 406 user=c.rhodecode_user)
406 407
407 408 Session().commit()
408 409 set_handle_flash_repos()
409 410 set_handle_flash_repo_groups()
410 411 set_handle_flash_user_groups()
411 412 h.flash(_('Successfully deleted user'), category='success')
412 413 except (UserOwnsReposException, UserOwnsRepoGroupsException,
413 414 UserOwnsUserGroupsException, DefaultUserException) as e:
414 415 h.flash(e, category='warning')
415 416 except Exception:
416 417 log.exception("Exception during deletion of user")
417 418 h.flash(_('An error occurred during deletion of user'),
418 419 category='error')
419 420 raise HTTPFound(h.route_path('users'))
420 421
421 422 @LoginRequired()
422 423 @HasPermissionAllDecorator('hg.admin')
423 424 @view_config(
424 425 route_name='user_edit', request_method='GET',
425 426 renderer='rhodecode:templates/admin/users/user_edit.mako')
426 427 def user_edit(self):
427 428 _ = self.request.translate
428 429 c = self.load_default_context()
429 430 c.user = self.db_user
430 431
431 432 c.active = 'profile'
432 433 c.extern_type = c.user.extern_type
433 434 c.extern_name = c.user.extern_name
434 435 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
435 436
436 437 defaults = c.user.get_dict()
437 438 defaults.update({'language': c.user.user_data.get('language')})
438 439
439 440 data = render(
440 441 'rhodecode:templates/admin/users/user_edit.mako',
441 442 self._get_template_context(c), self.request)
442 443 html = formencode.htmlfill.render(
443 444 data,
444 445 defaults=defaults,
445 446 encoding="UTF-8",
446 447 force_defaults=False
447 448 )
448 449 return Response(html)
449 450
450 451 @LoginRequired()
451 452 @HasPermissionAllDecorator('hg.admin')
452 453 @view_config(
453 454 route_name='user_edit_advanced', request_method='GET',
454 455 renderer='rhodecode:templates/admin/users/user_edit.mako')
455 456 def user_edit_advanced(self):
456 457 _ = self.request.translate
457 458 c = self.load_default_context()
458 459
459 460 user_id = self.db_user_id
460 461 c.user = self.db_user
461 462
462 463 c.active = 'advanced'
463 464 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
464 465 c.personal_repo_group_name = RepoGroupModel()\
465 466 .get_personal_group_name(c.user)
466 467
467 468 c.user_to_review_rules = sorted(
468 469 (x.user for x in c.user.user_review_rules),
469 470 key=lambda u: u.username.lower())
470 471
471 472 c.first_admin = User.get_first_super_admin()
472 473 defaults = c.user.get_dict()
473 474
474 475 # Interim workaround if the user participated on any pull requests as a
475 476 # reviewer.
476 477 has_review = len(c.user.reviewer_pull_requests)
477 478 c.can_delete_user = not has_review
478 479 c.can_delete_user_message = ''
479 480 inactive_link = h.link_to(
480 481 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
481 482 if has_review == 1:
482 483 c.can_delete_user_message = h.literal(_(
483 484 'The user participates as reviewer in {} pull request and '
484 485 'cannot be deleted. \nYou can set the user to '
485 486 '"{}" instead of deleting it.').format(
486 487 has_review, inactive_link))
487 488 elif has_review:
488 489 c.can_delete_user_message = h.literal(_(
489 490 'The user participates as reviewer in {} pull requests and '
490 491 'cannot be deleted. \nYou can set the user to '
491 492 '"{}" instead of deleting it.').format(
492 493 has_review, inactive_link))
493 494
494 495 data = render(
495 496 'rhodecode:templates/admin/users/user_edit.mako',
496 497 self._get_template_context(c), self.request)
497 498 html = formencode.htmlfill.render(
498 499 data,
499 500 defaults=defaults,
500 501 encoding="UTF-8",
501 502 force_defaults=False
502 503 )
503 504 return Response(html)
504 505
505 506 @LoginRequired()
506 507 @HasPermissionAllDecorator('hg.admin')
507 508 @view_config(
508 509 route_name='user_edit_global_perms', request_method='GET',
509 510 renderer='rhodecode:templates/admin/users/user_edit.mako')
510 511 def user_edit_global_perms(self):
511 512 _ = self.request.translate
512 513 c = self.load_default_context()
513 514 c.user = self.db_user
514 515
515 516 c.active = 'global_perms'
516 517
517 518 c.default_user = User.get_default_user()
518 519 defaults = c.user.get_dict()
519 520 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
520 521 defaults.update(c.default_user.get_default_perms())
521 522 defaults.update(c.user.get_default_perms())
522 523
523 524 data = render(
524 525 'rhodecode:templates/admin/users/user_edit.mako',
525 526 self._get_template_context(c), self.request)
526 527 html = formencode.htmlfill.render(
527 528 data,
528 529 defaults=defaults,
529 530 encoding="UTF-8",
530 531 force_defaults=False
531 532 )
532 533 return Response(html)
533 534
534 535 @LoginRequired()
535 536 @HasPermissionAllDecorator('hg.admin')
536 537 @CSRFRequired()
537 538 @view_config(
538 539 route_name='user_edit_global_perms_update', request_method='POST',
539 540 renderer='rhodecode:templates/admin/users/user_edit.mako')
540 541 def user_edit_global_perms_update(self):
541 542 _ = self.request.translate
542 543 c = self.load_default_context()
543 544
544 545 user_id = self.db_user_id
545 546 c.user = self.db_user
546 547
547 548 c.active = 'global_perms'
548 549 try:
549 550 # first stage that verifies the checkbox
550 551 _form = UserIndividualPermissionsForm(self.request.translate)
551 552 form_result = _form.to_python(dict(self.request.POST))
552 553 inherit_perms = form_result['inherit_default_permissions']
553 554 c.user.inherit_default_permissions = inherit_perms
554 555 Session().add(c.user)
555 556
556 557 if not inherit_perms:
557 558 # only update the individual ones if we un check the flag
558 559 _form = UserPermissionsForm(
559 560 self.request.translate,
560 561 [x[0] for x in c.repo_create_choices],
561 562 [x[0] for x in c.repo_create_on_write_choices],
562 563 [x[0] for x in c.repo_group_create_choices],
563 564 [x[0] for x in c.user_group_create_choices],
564 565 [x[0] for x in c.fork_choices],
565 566 [x[0] for x in c.inherit_default_permission_choices])()
566 567
567 568 form_result = _form.to_python(dict(self.request.POST))
568 569 form_result.update({'perm_user_id': c.user.user_id})
569 570
570 571 PermissionModel().update_user_permissions(form_result)
571 572
572 573 # TODO(marcink): implement global permissions
573 574 # audit_log.store_web('user.edit.permissions')
574 575
575 576 Session().commit()
577
576 578 h.flash(_('User global permissions updated successfully'),
577 579 category='success')
578 580
579 581 except formencode.Invalid as errors:
580 582 data = render(
581 583 'rhodecode:templates/admin/users/user_edit.mako',
582 584 self._get_template_context(c), self.request)
583 585 html = formencode.htmlfill.render(
584 586 data,
585 587 defaults=errors.value,
586 588 errors=errors.error_dict or {},
587 589 prefix_error=False,
588 590 encoding="UTF-8",
589 591 force_defaults=False
590 592 )
591 593 return Response(html)
592 594 except Exception:
593 595 log.exception("Exception during permissions saving")
594 596 h.flash(_('An error occurred during permissions saving'),
595 597 category='error')
598
599 affected_user_ids = [user_id]
600 events.trigger(events.UserPermissionsChange(affected_user_ids))
596 601 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
597 602
598 603 @LoginRequired()
599 604 @HasPermissionAllDecorator('hg.admin')
600 605 @CSRFRequired()
601 606 @view_config(
602 607 route_name='user_enable_force_password_reset', request_method='POST',
603 608 renderer='rhodecode:templates/admin/users/user_edit.mako')
604 609 def user_enable_force_password_reset(self):
605 610 _ = self.request.translate
606 611 c = self.load_default_context()
607 612
608 613 user_id = self.db_user_id
609 614 c.user = self.db_user
610 615
611 616 try:
612 617 c.user.update_userdata(force_password_change=True)
613 618
614 619 msg = _('Force password change enabled for user')
615 620 audit_logger.store_web('user.edit.password_reset.enabled',
616 621 user=c.rhodecode_user)
617 622
618 623 Session().commit()
619 624 h.flash(msg, category='success')
620 625 except Exception:
621 626 log.exception("Exception during password reset for user")
622 627 h.flash(_('An error occurred during password reset for user'),
623 628 category='error')
624 629
625 630 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
626 631
627 632 @LoginRequired()
628 633 @HasPermissionAllDecorator('hg.admin')
629 634 @CSRFRequired()
630 635 @view_config(
631 636 route_name='user_disable_force_password_reset', request_method='POST',
632 637 renderer='rhodecode:templates/admin/users/user_edit.mako')
633 638 def user_disable_force_password_reset(self):
634 639 _ = self.request.translate
635 640 c = self.load_default_context()
636 641
637 642 user_id = self.db_user_id
638 643 c.user = self.db_user
639 644
640 645 try:
641 646 c.user.update_userdata(force_password_change=False)
642 647
643 648 msg = _('Force password change disabled for user')
644 649 audit_logger.store_web(
645 650 'user.edit.password_reset.disabled',
646 651 user=c.rhodecode_user)
647 652
648 653 Session().commit()
649 654 h.flash(msg, category='success')
650 655 except Exception:
651 656 log.exception("Exception during password reset for user")
652 657 h.flash(_('An error occurred during password reset for user'),
653 658 category='error')
654 659
655 660 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
656 661
657 662 @LoginRequired()
658 663 @HasPermissionAllDecorator('hg.admin')
659 664 @CSRFRequired()
660 665 @view_config(
661 666 route_name='user_create_personal_repo_group', request_method='POST',
662 667 renderer='rhodecode:templates/admin/users/user_edit.mako')
663 668 def user_create_personal_repo_group(self):
664 669 """
665 670 Create personal repository group for this user
666 671 """
667 672 from rhodecode.model.repo_group import RepoGroupModel
668 673
669 674 _ = self.request.translate
670 675 c = self.load_default_context()
671 676
672 677 user_id = self.db_user_id
673 678 c.user = self.db_user
674 679
675 680 personal_repo_group = RepoGroup.get_user_personal_repo_group(
676 681 c.user.user_id)
677 682 if personal_repo_group:
678 683 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
679 684
680 685 personal_repo_group_name = RepoGroupModel().get_personal_group_name(
681 686 c.user)
682 687 named_personal_group = RepoGroup.get_by_group_name(
683 688 personal_repo_group_name)
684 689 try:
685 690
686 691 if named_personal_group and named_personal_group.user_id == c.user.user_id:
687 692 # migrate the same named group, and mark it as personal
688 693 named_personal_group.personal = True
689 694 Session().add(named_personal_group)
690 695 Session().commit()
691 696 msg = _('Linked repository group `%s` as personal' % (
692 697 personal_repo_group_name,))
693 698 h.flash(msg, category='success')
694 699 elif not named_personal_group:
695 700 RepoGroupModel().create_personal_repo_group(c.user)
696 701
697 702 msg = _('Created repository group `%s`' % (
698 703 personal_repo_group_name,))
699 704 h.flash(msg, category='success')
700 705 else:
701 706 msg = _('Repository group `%s` is already taken' % (
702 707 personal_repo_group_name,))
703 708 h.flash(msg, category='warning')
704 709 except Exception:
705 710 log.exception("Exception during repository group creation")
706 711 msg = _(
707 712 'An error occurred during repository group creation for user')
708 713 h.flash(msg, category='error')
709 714 Session().rollback()
710 715
711 716 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
712 717
713 718 @LoginRequired()
714 719 @HasPermissionAllDecorator('hg.admin')
715 720 @view_config(
716 721 route_name='edit_user_auth_tokens', request_method='GET',
717 722 renderer='rhodecode:templates/admin/users/user_edit.mako')
718 723 def auth_tokens(self):
719 724 _ = self.request.translate
720 725 c = self.load_default_context()
721 726 c.user = self.db_user
722 727
723 728 c.active = 'auth_tokens'
724 729
725 730 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
726 731 c.role_values = [
727 732 (x, AuthTokenModel.cls._get_role_name(x))
728 733 for x in AuthTokenModel.cls.ROLES]
729 734 c.role_options = [(c.role_values, _("Role"))]
730 735 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
731 736 c.user.user_id, show_expired=True)
732 737 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
733 738 return self._get_template_context(c)
734 739
735 740 def maybe_attach_token_scope(self, token):
736 741 # implemented in EE edition
737 742 pass
738 743
739 744 @LoginRequired()
740 745 @HasPermissionAllDecorator('hg.admin')
741 746 @CSRFRequired()
742 747 @view_config(
743 748 route_name='edit_user_auth_tokens_add', request_method='POST')
744 749 def auth_tokens_add(self):
745 750 _ = self.request.translate
746 751 c = self.load_default_context()
747 752
748 753 user_id = self.db_user_id
749 754 c.user = self.db_user
750 755
751 756 user_data = c.user.get_api_data()
752 757 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
753 758 description = self.request.POST.get('description')
754 759 role = self.request.POST.get('role')
755 760
756 761 token = UserModel().add_auth_token(
757 762 user=c.user.user_id,
758 763 lifetime_minutes=lifetime, role=role, description=description,
759 764 scope_callback=self.maybe_attach_token_scope)
760 765 token_data = token.get_api_data()
761 766
762 767 audit_logger.store_web(
763 768 'user.edit.token.add', action_data={
764 769 'data': {'token': token_data, 'user': user_data}},
765 770 user=self._rhodecode_user, )
766 771 Session().commit()
767 772
768 773 h.flash(_("Auth token successfully created"), category='success')
769 774 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
770 775
771 776 @LoginRequired()
772 777 @HasPermissionAllDecorator('hg.admin')
773 778 @CSRFRequired()
774 779 @view_config(
775 780 route_name='edit_user_auth_tokens_delete', request_method='POST')
776 781 def auth_tokens_delete(self):
777 782 _ = self.request.translate
778 783 c = self.load_default_context()
779 784
780 785 user_id = self.db_user_id
781 786 c.user = self.db_user
782 787
783 788 user_data = c.user.get_api_data()
784 789
785 790 del_auth_token = self.request.POST.get('del_auth_token')
786 791
787 792 if del_auth_token:
788 793 token = UserApiKeys.get_or_404(del_auth_token)
789 794 token_data = token.get_api_data()
790 795
791 796 AuthTokenModel().delete(del_auth_token, c.user.user_id)
792 797 audit_logger.store_web(
793 798 'user.edit.token.delete', action_data={
794 799 'data': {'token': token_data, 'user': user_data}},
795 800 user=self._rhodecode_user,)
796 801 Session().commit()
797 802 h.flash(_("Auth token successfully deleted"), category='success')
798 803
799 804 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
800 805
801 806 @LoginRequired()
802 807 @HasPermissionAllDecorator('hg.admin')
803 808 @view_config(
804 809 route_name='edit_user_ssh_keys', request_method='GET',
805 810 renderer='rhodecode:templates/admin/users/user_edit.mako')
806 811 def ssh_keys(self):
807 812 _ = self.request.translate
808 813 c = self.load_default_context()
809 814 c.user = self.db_user
810 815
811 816 c.active = 'ssh_keys'
812 817 c.default_key = self.request.GET.get('default_key')
813 818 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
814 819 return self._get_template_context(c)
815 820
816 821 @LoginRequired()
817 822 @HasPermissionAllDecorator('hg.admin')
818 823 @view_config(
819 824 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
820 825 renderer='rhodecode:templates/admin/users/user_edit.mako')
821 826 def ssh_keys_generate_keypair(self):
822 827 _ = self.request.translate
823 828 c = self.load_default_context()
824 829
825 830 c.user = self.db_user
826 831
827 832 c.active = 'ssh_keys_generate'
828 833 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
829 834 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
830 835
831 836 return self._get_template_context(c)
832 837
833 838 @LoginRequired()
834 839 @HasPermissionAllDecorator('hg.admin')
835 840 @CSRFRequired()
836 841 @view_config(
837 842 route_name='edit_user_ssh_keys_add', request_method='POST')
838 843 def ssh_keys_add(self):
839 844 _ = self.request.translate
840 845 c = self.load_default_context()
841 846
842 847 user_id = self.db_user_id
843 848 c.user = self.db_user
844 849
845 850 user_data = c.user.get_api_data()
846 851 key_data = self.request.POST.get('key_data')
847 852 description = self.request.POST.get('description')
848 853
849 854 fingerprint = 'unknown'
850 855 try:
851 856 if not key_data:
852 857 raise ValueError('Please add a valid public key')
853 858
854 859 key = SshKeyModel().parse_key(key_data.strip())
855 860 fingerprint = key.hash_md5()
856 861
857 862 ssh_key = SshKeyModel().create(
858 863 c.user.user_id, fingerprint, key.keydata, description)
859 864 ssh_key_data = ssh_key.get_api_data()
860 865
861 866 audit_logger.store_web(
862 867 'user.edit.ssh_key.add', action_data={
863 868 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
864 869 user=self._rhodecode_user, )
865 870 Session().commit()
866 871
867 872 # Trigger an event on change of keys.
868 873 trigger(SshKeyFileChangeEvent(), self.request.registry)
869 874
870 875 h.flash(_("Ssh Key successfully created"), category='success')
871 876
872 877 except IntegrityError:
873 878 log.exception("Exception during ssh key saving")
874 879 err = 'Such key with fingerprint `{}` already exists, ' \
875 880 'please use a different one'.format(fingerprint)
876 881 h.flash(_('An error occurred during ssh key saving: {}').format(err),
877 882 category='error')
878 883 except Exception as e:
879 884 log.exception("Exception during ssh key saving")
880 885 h.flash(_('An error occurred during ssh key saving: {}').format(e),
881 886 category='error')
882 887
883 888 return HTTPFound(
884 889 h.route_path('edit_user_ssh_keys', user_id=user_id))
885 890
886 891 @LoginRequired()
887 892 @HasPermissionAllDecorator('hg.admin')
888 893 @CSRFRequired()
889 894 @view_config(
890 895 route_name='edit_user_ssh_keys_delete', request_method='POST')
891 896 def ssh_keys_delete(self):
892 897 _ = self.request.translate
893 898 c = self.load_default_context()
894 899
895 900 user_id = self.db_user_id
896 901 c.user = self.db_user
897 902
898 903 user_data = c.user.get_api_data()
899 904
900 905 del_ssh_key = self.request.POST.get('del_ssh_key')
901 906
902 907 if del_ssh_key:
903 908 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
904 909 ssh_key_data = ssh_key.get_api_data()
905 910
906 911 SshKeyModel().delete(del_ssh_key, c.user.user_id)
907 912 audit_logger.store_web(
908 913 'user.edit.ssh_key.delete', action_data={
909 914 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
910 915 user=self._rhodecode_user,)
911 916 Session().commit()
912 917 # Trigger an event on change of keys.
913 918 trigger(SshKeyFileChangeEvent(), self.request.registry)
914 919 h.flash(_("Ssh key successfully deleted"), category='success')
915 920
916 921 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
917 922
918 923 @LoginRequired()
919 924 @HasPermissionAllDecorator('hg.admin')
920 925 @view_config(
921 926 route_name='edit_user_emails', request_method='GET',
922 927 renderer='rhodecode:templates/admin/users/user_edit.mako')
923 928 def emails(self):
924 929 _ = self.request.translate
925 930 c = self.load_default_context()
926 931 c.user = self.db_user
927 932
928 933 c.active = 'emails'
929 934 c.user_email_map = UserEmailMap.query() \
930 935 .filter(UserEmailMap.user == c.user).all()
931 936
932 937 return self._get_template_context(c)
933 938
934 939 @LoginRequired()
935 940 @HasPermissionAllDecorator('hg.admin')
936 941 @CSRFRequired()
937 942 @view_config(
938 943 route_name='edit_user_emails_add', request_method='POST')
939 944 def emails_add(self):
940 945 _ = self.request.translate
941 946 c = self.load_default_context()
942 947
943 948 user_id = self.db_user_id
944 949 c.user = self.db_user
945 950
946 951 email = self.request.POST.get('new_email')
947 952 user_data = c.user.get_api_data()
948 953 try:
949 954
950 955 form = UserExtraEmailForm(self.request.translate)()
951 956 data = form.to_python({'email': email})
952 957 email = data['email']
953 958
954 959 UserModel().add_extra_email(c.user.user_id, email)
955 960 audit_logger.store_web(
956 961 'user.edit.email.add',
957 962 action_data={'email': email, 'user': user_data},
958 963 user=self._rhodecode_user)
959 964 Session().commit()
960 965 h.flash(_("Added new email address `%s` for user account") % email,
961 966 category='success')
962 967 except formencode.Invalid as error:
963 968 h.flash(h.escape(error.error_dict['email']), category='error')
964 969 except IntegrityError:
965 970 log.warning("Email %s already exists", email)
966 971 h.flash(_('Email `{}` is already registered for another user.').format(email),
967 972 category='error')
968 973 except Exception:
969 974 log.exception("Exception during email saving")
970 975 h.flash(_('An error occurred during email saving'),
971 976 category='error')
972 977 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
973 978
974 979 @LoginRequired()
975 980 @HasPermissionAllDecorator('hg.admin')
976 981 @CSRFRequired()
977 982 @view_config(
978 983 route_name='edit_user_emails_delete', request_method='POST')
979 984 def emails_delete(self):
980 985 _ = self.request.translate
981 986 c = self.load_default_context()
982 987
983 988 user_id = self.db_user_id
984 989 c.user = self.db_user
985 990
986 991 email_id = self.request.POST.get('del_email_id')
987 992 user_model = UserModel()
988 993
989 994 email = UserEmailMap.query().get(email_id).email
990 995 user_data = c.user.get_api_data()
991 996 user_model.delete_extra_email(c.user.user_id, email_id)
992 997 audit_logger.store_web(
993 998 'user.edit.email.delete',
994 999 action_data={'email': email, 'user': user_data},
995 1000 user=self._rhodecode_user)
996 1001 Session().commit()
997 1002 h.flash(_("Removed email address from user account"),
998 1003 category='success')
999 1004 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
1000 1005
1001 1006 @LoginRequired()
1002 1007 @HasPermissionAllDecorator('hg.admin')
1003 1008 @view_config(
1004 1009 route_name='edit_user_ips', request_method='GET',
1005 1010 renderer='rhodecode:templates/admin/users/user_edit.mako')
1006 1011 def ips(self):
1007 1012 _ = self.request.translate
1008 1013 c = self.load_default_context()
1009 1014 c.user = self.db_user
1010 1015
1011 1016 c.active = 'ips'
1012 1017 c.user_ip_map = UserIpMap.query() \
1013 1018 .filter(UserIpMap.user == c.user).all()
1014 1019
1015 1020 c.inherit_default_ips = c.user.inherit_default_permissions
1016 1021 c.default_user_ip_map = UserIpMap.query() \
1017 1022 .filter(UserIpMap.user == User.get_default_user()).all()
1018 1023
1019 1024 return self._get_template_context(c)
1020 1025
1021 1026 @LoginRequired()
1022 1027 @HasPermissionAllDecorator('hg.admin')
1023 1028 @CSRFRequired()
1024 1029 @view_config(
1025 1030 route_name='edit_user_ips_add', request_method='POST')
1026 1031 # NOTE(marcink): this view is allowed for default users, as we can
1027 1032 # edit their IP white list
1028 1033 def ips_add(self):
1029 1034 _ = self.request.translate
1030 1035 c = self.load_default_context()
1031 1036
1032 1037 user_id = self.db_user_id
1033 1038 c.user = self.db_user
1034 1039
1035 1040 user_model = UserModel()
1036 1041 desc = self.request.POST.get('description')
1037 1042 try:
1038 1043 ip_list = user_model.parse_ip_range(
1039 1044 self.request.POST.get('new_ip'))
1040 1045 except Exception as e:
1041 1046 ip_list = []
1042 1047 log.exception("Exception during ip saving")
1043 1048 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1044 1049 category='error')
1045 1050 added = []
1046 1051 user_data = c.user.get_api_data()
1047 1052 for ip in ip_list:
1048 1053 try:
1049 1054 form = UserExtraIpForm(self.request.translate)()
1050 1055 data = form.to_python({'ip': ip})
1051 1056 ip = data['ip']
1052 1057
1053 1058 user_model.add_extra_ip(c.user.user_id, ip, desc)
1054 1059 audit_logger.store_web(
1055 1060 'user.edit.ip.add',
1056 1061 action_data={'ip': ip, 'user': user_data},
1057 1062 user=self._rhodecode_user)
1058 1063 Session().commit()
1059 1064 added.append(ip)
1060 1065 except formencode.Invalid as error:
1061 1066 msg = error.error_dict['ip']
1062 1067 h.flash(msg, category='error')
1063 1068 except Exception:
1064 1069 log.exception("Exception during ip saving")
1065 1070 h.flash(_('An error occurred during ip saving'),
1066 1071 category='error')
1067 1072 if added:
1068 1073 h.flash(
1069 1074 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1070 1075 category='success')
1071 1076 if 'default_user' in self.request.POST:
1072 1077 # case for editing global IP list we do it for 'DEFAULT' user
1073 1078 raise HTTPFound(h.route_path('admin_permissions_ips'))
1074 1079 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1075 1080
1076 1081 @LoginRequired()
1077 1082 @HasPermissionAllDecorator('hg.admin')
1078 1083 @CSRFRequired()
1079 1084 @view_config(
1080 1085 route_name='edit_user_ips_delete', request_method='POST')
1081 1086 # NOTE(marcink): this view is allowed for default users, as we can
1082 1087 # edit their IP white list
1083 1088 def ips_delete(self):
1084 1089 _ = self.request.translate
1085 1090 c = self.load_default_context()
1086 1091
1087 1092 user_id = self.db_user_id
1088 1093 c.user = self.db_user
1089 1094
1090 1095 ip_id = self.request.POST.get('del_ip_id')
1091 1096 user_model = UserModel()
1092 1097 user_data = c.user.get_api_data()
1093 1098 ip = UserIpMap.query().get(ip_id).ip_addr
1094 1099 user_model.delete_extra_ip(c.user.user_id, ip_id)
1095 1100 audit_logger.store_web(
1096 1101 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1097 1102 user=self._rhodecode_user)
1098 1103 Session().commit()
1099 1104 h.flash(_("Removed ip address from user whitelist"), category='success')
1100 1105
1101 1106 if 'default_user' in self.request.POST:
1102 1107 # case for editing global IP list we do it for 'DEFAULT' user
1103 1108 raise HTTPFound(h.route_path('admin_permissions_ips'))
1104 1109 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1105 1110
1106 1111 @LoginRequired()
1107 1112 @HasPermissionAllDecorator('hg.admin')
1108 1113 @view_config(
1109 1114 route_name='edit_user_groups_management', request_method='GET',
1110 1115 renderer='rhodecode:templates/admin/users/user_edit.mako')
1111 1116 def groups_management(self):
1112 1117 c = self.load_default_context()
1113 1118 c.user = self.db_user
1114 1119 c.data = c.user.group_member
1115 1120
1116 1121 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1117 1122 for group in c.user.group_member]
1118 1123 c.groups = json.dumps(groups)
1119 1124 c.active = 'groups'
1120 1125
1121 1126 return self._get_template_context(c)
1122 1127
1123 1128 @LoginRequired()
1124 1129 @HasPermissionAllDecorator('hg.admin')
1125 1130 @CSRFRequired()
1126 1131 @view_config(
1127 1132 route_name='edit_user_groups_management_updates', request_method='POST')
1128 1133 def groups_management_updates(self):
1129 1134 _ = self.request.translate
1130 1135 c = self.load_default_context()
1131 1136
1132 1137 user_id = self.db_user_id
1133 1138 c.user = self.db_user
1134 1139
1135 1140 user_groups = set(self.request.POST.getall('users_group_id'))
1136 1141 user_groups_objects = []
1137 1142
1138 1143 for ugid in user_groups:
1139 1144 user_groups_objects.append(
1140 1145 UserGroupModel().get_group(safe_int(ugid)))
1141 1146 user_group_model = UserGroupModel()
1142 1147 added_to_groups, removed_from_groups = \
1143 1148 user_group_model.change_groups(c.user, user_groups_objects)
1144 1149
1145 1150 user_data = c.user.get_api_data()
1146 1151 for user_group_id in added_to_groups:
1147 1152 user_group = UserGroup.get(user_group_id)
1148 1153 old_values = user_group.get_api_data()
1149 1154 audit_logger.store_web(
1150 1155 'user_group.edit.member.add',
1151 1156 action_data={'user': user_data, 'old_data': old_values},
1152 1157 user=self._rhodecode_user)
1153 1158
1154 1159 for user_group_id in removed_from_groups:
1155 1160 user_group = UserGroup.get(user_group_id)
1156 1161 old_values = user_group.get_api_data()
1157 1162 audit_logger.store_web(
1158 1163 'user_group.edit.member.delete',
1159 1164 action_data={'user': user_data, 'old_data': old_values},
1160 1165 user=self._rhodecode_user)
1161 1166
1162 1167 Session().commit()
1163 1168 c.active = 'user_groups_management'
1164 1169 h.flash(_("Groups successfully changed"), category='success')
1165 1170
1166 1171 return HTTPFound(h.route_path(
1167 1172 'edit_user_groups_management', user_id=user_id))
1168 1173
1169 1174 @LoginRequired()
1170 1175 @HasPermissionAllDecorator('hg.admin')
1171 1176 @view_config(
1172 1177 route_name='edit_user_audit_logs', request_method='GET',
1173 1178 renderer='rhodecode:templates/admin/users/user_edit.mako')
1174 1179 def user_audit_logs(self):
1175 1180 _ = self.request.translate
1176 1181 c = self.load_default_context()
1177 1182 c.user = self.db_user
1178 1183
1179 1184 c.active = 'audit'
1180 1185
1181 1186 p = safe_int(self.request.GET.get('page', 1), 1)
1182 1187
1183 1188 filter_term = self.request.GET.get('filter')
1184 1189 user_log = UserModel().get_user_log(c.user, filter_term)
1185 1190
1186 1191 def url_generator(**kw):
1187 1192 if filter_term:
1188 1193 kw['filter'] = filter_term
1189 1194 return self.request.current_route_path(_query=kw)
1190 1195
1191 1196 c.audit_logs = h.Page(
1192 1197 user_log, page=p, items_per_page=10, url=url_generator)
1193 1198 c.filter_term = filter_term
1194 1199 return self._get_template_context(c)
1195 1200
1196 1201 @LoginRequired()
1197 1202 @HasPermissionAllDecorator('hg.admin')
1198 1203 @view_config(
1199 1204 route_name='edit_user_perms_summary', request_method='GET',
1200 1205 renderer='rhodecode:templates/admin/users/user_edit.mako')
1201 1206 def user_perms_summary(self):
1202 1207 _ = self.request.translate
1203 1208 c = self.load_default_context()
1204 1209 c.user = self.db_user
1205 1210
1206 1211 c.active = 'perms_summary'
1207 1212 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1208 1213
1209 1214 return self._get_template_context(c)
1210 1215
1211 1216 @LoginRequired()
1212 1217 @HasPermissionAllDecorator('hg.admin')
1213 1218 @view_config(
1214 1219 route_name='edit_user_perms_summary_json', request_method='GET',
1215 1220 renderer='json_ext')
1216 1221 def user_perms_summary_json(self):
1217 1222 self.load_default_context()
1218 1223 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1219 1224
1220 1225 return perm_user.permissions
1221 1226
1222 1227 @LoginRequired()
1223 1228 @HasPermissionAllDecorator('hg.admin')
1224 1229 @view_config(
1225 1230 route_name='edit_user_caches', request_method='GET',
1226 1231 renderer='rhodecode:templates/admin/users/user_edit.mako')
1227 1232 def user_caches(self):
1228 1233 _ = self.request.translate
1229 1234 c = self.load_default_context()
1230 1235 c.user = self.db_user
1231 1236
1232 1237 c.active = 'caches'
1233 1238 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1234 1239
1235 1240 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1236 1241 c.region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1237 1242 c.backend = c.region.backend
1238 1243 c.user_keys = sorted(c.region.backend.list_keys(prefix=cache_namespace_uid))
1239 1244
1240 1245 return self._get_template_context(c)
1241 1246
1242 1247 @LoginRequired()
1243 1248 @HasPermissionAllDecorator('hg.admin')
1244 1249 @CSRFRequired()
1245 1250 @view_config(
1246 1251 route_name='edit_user_caches_update', request_method='POST')
1247 1252 def user_caches_update(self):
1248 1253 _ = self.request.translate
1249 1254 c = self.load_default_context()
1250 1255 c.user = self.db_user
1251 1256
1252 1257 c.active = 'caches'
1253 1258 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1254 1259
1255 1260 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1256 1261 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid)
1257 1262
1258 1263 h.flash(_("Deleted {} cache keys").format(del_keys), category='success')
1259 1264
1260 1265 return HTTPFound(h.route_path(
1261 1266 'edit_user_caches', user_id=c.user.user_id))
@@ -1,189 +1,193 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import deform
23 23
24 24 from pyramid.view import view_config
25 25 from pyramid.httpexceptions import HTTPFound
26 26
27 27 from rhodecode import events
28 28 from rhodecode.apps._base import RepoGroupAppView
29 29 from rhodecode.forms import RcForm
30 30 from rhodecode.lib import helpers as h
31 31 from rhodecode.lib import audit_logger
32 32 from rhodecode.lib.auth import (
33 33 LoginRequired, HasPermissionAll,
34 34 HasRepoGroupPermissionAny, HasRepoGroupPermissionAnyDecorator, CSRFRequired)
35 35 from rhodecode.model.db import Session, RepoGroup, User
36 36 from rhodecode.model.scm import RepoGroupList
37 37 from rhodecode.model.repo_group import RepoGroupModel
38 38 from rhodecode.model.validation_schema.schemas import repo_group_schema
39 39
40 40 log = logging.getLogger(__name__)
41 41
42 42
43 43 class RepoGroupSettingsView(RepoGroupAppView):
44 44 def load_default_context(self):
45 45 c = self._get_local_tmpl_context()
46 46 c.repo_group = self.db_repo_group
47 47 no_parrent = not c.repo_group.parent_group
48 48 can_create_in_root = self._can_create_repo_group()
49 49
50 50 show_root_location = False
51 51 if no_parrent or can_create_in_root:
52 52 # we're global admin, we're ok and we can create TOP level groups
53 53 # or in case this group is already at top-level we also allow
54 54 # creation in root
55 55 show_root_location = True
56 56
57 57 acl_groups = RepoGroupList(
58 58 RepoGroup.query().all(),
59 59 perm_set=['group.admin'])
60 60 c.repo_groups = RepoGroup.groups_choices(
61 61 groups=acl_groups,
62 62 show_empty_group=show_root_location)
63 63 # filter out current repo group
64 64 exclude_group_ids = [c.repo_group.group_id]
65 65 c.repo_groups = filter(lambda x: x[0] not in exclude_group_ids,
66 66 c.repo_groups)
67 67 c.repo_groups_choices = map(lambda k: k[0], c.repo_groups)
68 68
69 69 parent_group = c.repo_group.parent_group
70 70
71 71 add_parent_group = (parent_group and (
72 72 parent_group.group_id not in c.repo_groups_choices))
73 73 if add_parent_group:
74 74 c.repo_groups_choices.append(parent_group.group_id)
75 75 c.repo_groups.append(RepoGroup._generate_choice(parent_group))
76 76 return c
77 77
78 78 def _can_create_repo_group(self, parent_group_id=None):
79 79 is_admin = HasPermissionAll('hg.admin')('group create controller')
80 80 create_repo_group = HasPermissionAll(
81 81 'hg.repogroup.create.true')('group create controller')
82 82 if is_admin or (create_repo_group and not parent_group_id):
83 83 # we're global admin, or we have global repo group create
84 84 # permission
85 85 # we're ok and we can create TOP level groups
86 86 return True
87 87 elif parent_group_id:
88 88 # we check the permission if we can write to parent group
89 89 group = RepoGroup.get(parent_group_id)
90 90 group_name = group.group_name if group else None
91 91 if HasRepoGroupPermissionAny('group.admin')(
92 92 group_name, 'check if user is an admin of group'):
93 93 # we're an admin of passed in group, we're ok.
94 94 return True
95 95 else:
96 96 return False
97 97 return False
98 98
99 99 def _get_schema(self, c, old_values=None):
100 100 return repo_group_schema.RepoGroupSettingsSchema().bind(
101 101 repo_group_repo_group_options=c.repo_groups_choices,
102 102 repo_group_repo_group_items=c.repo_groups,
103 103
104 104 # user caller
105 105 user=self._rhodecode_user,
106 106 old_values=old_values
107 107 )
108 108
109 109 @LoginRequired()
110 110 @HasRepoGroupPermissionAnyDecorator('group.admin')
111 111 @view_config(
112 112 route_name='edit_repo_group', request_method='GET',
113 113 renderer='rhodecode:templates/admin/repo_groups/repo_group_edit.mako')
114 114 def edit_settings(self):
115 115 c = self.load_default_context()
116 116 c.active = 'settings'
117 117
118 118 defaults = RepoGroupModel()._get_defaults(self.db_repo_group_name)
119 119 defaults['repo_group_owner'] = defaults['user']
120 120
121 121 schema = self._get_schema(c)
122 122 c.form = RcForm(schema, appstruct=defaults)
123 123 return self._get_template_context(c)
124 124
125 125 @LoginRequired()
126 126 @HasRepoGroupPermissionAnyDecorator('group.admin')
127 127 @CSRFRequired()
128 128 @view_config(
129 129 route_name='edit_repo_group', request_method='POST',
130 130 renderer='rhodecode:templates/admin/repo_groups/repo_group_edit.mako')
131 131 def edit_settings_update(self):
132 132 _ = self.request.translate
133 133 c = self.load_default_context()
134 134 c.active = 'settings'
135 135
136 136 old_repo_group_name = self.db_repo_group_name
137 137 new_repo_group_name = old_repo_group_name
138 138
139 139 old_values = RepoGroupModel()._get_defaults(self.db_repo_group_name)
140 140 schema = self._get_schema(c, old_values=old_values)
141 141
142 142 c.form = RcForm(schema)
143 143 pstruct = self.request.POST.items()
144 144
145 145 try:
146 146 schema_data = c.form.validate(pstruct)
147 147 except deform.ValidationFailure as err_form:
148 148 return self._get_template_context(c)
149 149
150 150 # data is now VALID, proceed with updates
151 151 # save validated data back into the updates dict
152 152 validated_updates = dict(
153 153 group_name=schema_data['repo_group']['repo_group_name_without_group'],
154 154 group_parent_id=schema_data['repo_group']['repo_group_id'],
155 155 user=schema_data['repo_group_owner'],
156 156 group_description=schema_data['repo_group_description'],
157 157 enable_locking=schema_data['repo_group_enable_locking'],
158 158 )
159 159
160 160 try:
161 161 RepoGroupModel().update(self.db_repo_group, validated_updates)
162 162
163 163 audit_logger.store_web(
164 164 'repo_group.edit', action_data={'old_data': old_values},
165 165 user=c.rhodecode_user)
166 166
167 167 Session().commit()
168 168
169 169 # use the new full name for redirect once we know we updated
170 170 # the name on filesystem and in DB
171 171 new_repo_group_name = schema_data['repo_group']['repo_group_name_with_group']
172 172
173 173 h.flash(_('Repository Group `{}` updated successfully').format(
174 174 old_repo_group_name), category='success')
175 175
176 176 except Exception:
177 177 log.exception("Exception during update or repository group")
178 178 h.flash(_('Error occurred during update of repository group %s')
179 179 % old_repo_group_name, category='error')
180 180
181 181 name_changed = old_repo_group_name != new_repo_group_name
182 182 if name_changed:
183 current_perms = self.db_repo_group.permissions(expand_from_user_groups=True)
184 affected_user_ids = [perm['user_id'] for perm in current_perms]
185
186 # NOTE(marcink): also add owner maybe it has changed
183 187 owner = User.get_by_username(schema_data['repo_group_owner'])
184 188 owner_id = owner.user_id if owner else self._rhodecode_user.user_id
185 events.trigger(events.UserPermissionsChange([
186 self._rhodecode_user.user_id, owner_id]))
189 affected_user_ids.extend([self._rhodecode_user.user_id, owner_id])
190 events.trigger(events.UserPermissionsChange(affected_user_ids))
187 191
188 192 raise HTTPFound(
189 193 h.route_path('edit_repo_group', repo_group_name=new_repo_group_name))
@@ -1,263 +1,270 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import datetime
23 23 import formencode
24 24 import formencode.htmlfill
25 25
26 26 from pyramid.httpexceptions import HTTPFound
27 27 from pyramid.view import view_config
28 28 from pyramid.renderers import render
29 29 from pyramid.response import Response
30 30
31 31 from rhodecode import events
32 32 from rhodecode.apps._base import RepoAppView, DataGridAppView
33 33 from rhodecode.lib.auth import (
34 34 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous,
35 35 HasRepoPermissionAny, HasPermissionAnyDecorator, CSRFRequired)
36 36 import rhodecode.lib.helpers as h
37 37 from rhodecode.lib.celerylib.utils import get_task_id
38 38 from rhodecode.model.db import coalesce, or_, Repository, RepoGroup
39 39 from rhodecode.model.repo import RepoModel
40 40 from rhodecode.model.forms import RepoForkForm
41 41 from rhodecode.model.scm import ScmModel, RepoGroupList
42 42 from rhodecode.lib.utils2 import safe_int, safe_unicode
43 43
44 44 log = logging.getLogger(__name__)
45 45
46 46
47 47 class RepoForksView(RepoAppView, DataGridAppView):
48 48
49 49 def load_default_context(self):
50 50 c = self._get_local_tmpl_context(include_app_defaults=True)
51 51 c.rhodecode_repo = self.rhodecode_vcs_repo
52 52
53 53 acl_groups = RepoGroupList(
54 54 RepoGroup.query().all(),
55 55 perm_set=['group.write', 'group.admin'])
56 56 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
57 57 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
58 58 choices, c.landing_revs = ScmModel().get_repo_landing_revs(
59 59 self.request.translate)
60 60 c.landing_revs_choices = choices
61 61 c.personal_repo_group = c.rhodecode_user.personal_repo_group
62 62
63 63 return c
64 64
65 65 @LoginRequired()
66 66 @HasRepoPermissionAnyDecorator(
67 67 'repository.read', 'repository.write', 'repository.admin')
68 68 @view_config(
69 69 route_name='repo_forks_show_all', request_method='GET',
70 70 renderer='rhodecode:templates/forks/forks.mako')
71 71 def repo_forks_show_all(self):
72 72 c = self.load_default_context()
73 73 return self._get_template_context(c)
74 74
75 75 @LoginRequired()
76 76 @HasRepoPermissionAnyDecorator(
77 77 'repository.read', 'repository.write', 'repository.admin')
78 78 @view_config(
79 79 route_name='repo_forks_data', request_method='GET',
80 80 renderer='json_ext', xhr=True)
81 81 def repo_forks_data(self):
82 82 _ = self.request.translate
83 83 self.load_default_context()
84 84 column_map = {
85 85 'fork_name': 'repo_name',
86 86 'fork_date': 'created_on',
87 87 'last_activity': 'updated_on'
88 88 }
89 89 draw, start, limit = self._extract_chunk(self.request)
90 90 search_q, order_by, order_dir = self._extract_ordering(
91 91 self.request, column_map=column_map)
92 92
93 93 acl_check = HasRepoPermissionAny(
94 94 'repository.read', 'repository.write', 'repository.admin')
95 95 repo_id = self.db_repo.repo_id
96 96 allowed_ids = [-1]
97 97 for f in Repository.query().filter(Repository.fork_id == repo_id):
98 98 if acl_check(f.repo_name, 'get forks check'):
99 99 allowed_ids.append(f.repo_id)
100 100
101 101 forks_data_total_count = Repository.query()\
102 102 .filter(Repository.fork_id == repo_id)\
103 103 .filter(Repository.repo_id.in_(allowed_ids))\
104 104 .count()
105 105
106 106 # json generate
107 107 base_q = Repository.query()\
108 108 .filter(Repository.fork_id == repo_id)\
109 109 .filter(Repository.repo_id.in_(allowed_ids))\
110 110
111 111 if search_q:
112 112 like_expression = u'%{}%'.format(safe_unicode(search_q))
113 113 base_q = base_q.filter(or_(
114 114 Repository.repo_name.ilike(like_expression),
115 115 Repository.description.ilike(like_expression),
116 116 ))
117 117
118 118 forks_data_total_filtered_count = base_q.count()
119 119
120 120 sort_col = getattr(Repository, order_by, None)
121 121 if sort_col:
122 122 if order_dir == 'asc':
123 123 # handle null values properly to order by NULL last
124 124 if order_by in ['last_activity']:
125 125 sort_col = coalesce(sort_col, datetime.date.max)
126 126 sort_col = sort_col.asc()
127 127 else:
128 128 # handle null values properly to order by NULL last
129 129 if order_by in ['last_activity']:
130 130 sort_col = coalesce(sort_col, datetime.date.min)
131 131 sort_col = sort_col.desc()
132 132
133 133 base_q = base_q.order_by(sort_col)
134 134 base_q = base_q.offset(start).limit(limit)
135 135
136 136 fork_list = base_q.all()
137 137
138 138 def fork_actions(fork):
139 139 url_link = h.route_path(
140 140 'repo_compare',
141 141 repo_name=fork.repo_name,
142 142 source_ref_type=self.db_repo.landing_rev[0],
143 143 source_ref=self.db_repo.landing_rev[1],
144 144 target_ref_type=self.db_repo.landing_rev[0],
145 145 target_ref=self.db_repo.landing_rev[1],
146 146 _query=dict(merge=1, target_repo=f.repo_name))
147 147 return h.link_to(_('Compare fork'), url_link, class_='btn-link')
148 148
149 149 def fork_name(fork):
150 150 return h.link_to(fork.repo_name,
151 151 h.route_path('repo_summary', repo_name=fork.repo_name))
152 152
153 153 forks_data = []
154 154 for fork in fork_list:
155 155 forks_data.append({
156 156 "username": h.gravatar_with_user(self.request, fork.user.username),
157 157 "fork_name": fork_name(fork),
158 158 "description": fork.description_safe,
159 159 "fork_date": h.age_component(fork.created_on, time_is_local=True),
160 160 "last_activity": h.format_date(fork.updated_on),
161 161 "action": fork_actions(fork),
162 162 })
163 163
164 164 data = ({
165 165 'draw': draw,
166 166 'data': forks_data,
167 167 'recordsTotal': forks_data_total_count,
168 168 'recordsFiltered': forks_data_total_filtered_count,
169 169 })
170 170
171 171 return data
172 172
173 173 @LoginRequired()
174 174 @NotAnonymous()
175 175 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
176 176 @HasRepoPermissionAnyDecorator(
177 177 'repository.read', 'repository.write', 'repository.admin')
178 178 @view_config(
179 179 route_name='repo_fork_new', request_method='GET',
180 180 renderer='rhodecode:templates/forks/forks.mako')
181 181 def repo_fork_new(self):
182 182 c = self.load_default_context()
183 183
184 184 defaults = RepoModel()._get_defaults(self.db_repo_name)
185 185 # alter the description to indicate a fork
186 186 defaults['description'] = (
187 187 'fork of repository: %s \n%s' % (
188 188 defaults['repo_name'], defaults['description']))
189 189 # add suffix to fork
190 190 defaults['repo_name'] = '%s-fork' % defaults['repo_name']
191 191
192 192 data = render('rhodecode:templates/forks/fork.mako',
193 193 self._get_template_context(c), self.request)
194 194 html = formencode.htmlfill.render(
195 195 data,
196 196 defaults=defaults,
197 197 encoding="UTF-8",
198 198 force_defaults=False
199 199 )
200 200 return Response(html)
201 201
202 202 @LoginRequired()
203 203 @NotAnonymous()
204 204 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
205 205 @HasRepoPermissionAnyDecorator(
206 206 'repository.read', 'repository.write', 'repository.admin')
207 207 @CSRFRequired()
208 208 @view_config(
209 209 route_name='repo_fork_create', request_method='POST',
210 210 renderer='rhodecode:templates/forks/fork.mako')
211 211 def repo_fork_create(self):
212 212 _ = self.request.translate
213 213 c = self.load_default_context()
214 214
215 215 _form = RepoForkForm(self.request.translate, old_data={'repo_type': self.db_repo.repo_type},
216 216 repo_groups=c.repo_groups_choices,
217 217 landing_revs=c.landing_revs_choices)()
218 218 post_data = dict(self.request.POST)
219 219
220 220 # forbid injecting other repo by forging a request
221 221 post_data['fork_parent_id'] = self.db_repo.repo_id
222 222
223 223 form_result = {}
224 224 task_id = None
225 225 try:
226 226 form_result = _form.to_python(post_data)
227 copy_permissions = form_result.get('copy_permissions')
227 228 # create fork is done sometimes async on celery, db transaction
228 229 # management is handled there.
229 230 task = RepoModel().create_fork(
230 231 form_result, c.rhodecode_user.user_id)
231 232
232 233 task_id = get_task_id(task)
233 234 except formencode.Invalid as errors:
234 235 c.rhodecode_db_repo = self.db_repo
235 236
236 237 data = render('rhodecode:templates/forks/fork.mako',
237 238 self._get_template_context(c), self.request)
238 239 html = formencode.htmlfill.render(
239 240 data,
240 241 defaults=errors.value,
241 242 errors=errors.error_dict or {},
242 243 prefix_error=False,
243 244 encoding="UTF-8",
244 245 force_defaults=False
245 246 )
246 247 return Response(html)
247 248 except Exception:
248 249 log.exception(
249 u'Exception while trying to fork the repository %s',
250 self.db_repo_name)
251 msg = (
252 _('An error occurred during repository forking %s') % (
253 self.db_repo_name, ))
250 u'Exception while trying to fork the repository %s', self.db_repo_name)
251 msg = _('An error occurred during repository forking %s') % (self.db_repo_name, )
254 252 h.flash(msg, category='error')
253 raise HTTPFound(h.route_path('home'))
255 254
256 255 repo_name = form_result.get('repo_name_full', self.db_repo_name)
257 256
258 events.trigger(events.UserPermissionsChange([self._rhodecode_user.user_id]))
257 affected_user_ids = [self._rhodecode_user.user_id]
258 if copy_permissions:
259 repository = Repository.get_by_repo_name(repo_name)
260 # also include those newly created by copy
261 user_group_perms = repository.permissions(expand_from_user_groups=True)
262 copy_perms = [perm['user_id'] for perm in user_group_perms]
263 # also include those newly created by copy
264 affected_user_ids.extend(copy_perms)
265
266 events.trigger(events.UserPermissionsChange(affected_user_ids))
259 267
260 268 raise HTTPFound(
261 h.route_path('repo_creating',
262 repo_name=repo_name,
269 h.route_path('repo_creating', repo_name=repo_name,
263 270 _query=dict(task_id=task_id)))
@@ -1,262 +1,266 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22
23 23 import deform
24 24 from pyramid.httpexceptions import HTTPFound
25 25 from pyramid.view import view_config
26 26
27 27 from rhodecode import events
28 28 from rhodecode.apps._base import RepoAppView
29 29 from rhodecode.forms import RcForm
30 30 from rhodecode.lib import helpers as h
31 31 from rhodecode.lib import audit_logger
32 32 from rhodecode.lib.auth import (
33 33 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
34 34 from rhodecode.model.db import RepositoryField, RepoGroup, Repository, User
35 35 from rhodecode.model.meta import Session
36 36 from rhodecode.model.repo import RepoModel
37 37 from rhodecode.model.scm import RepoGroupList, ScmModel
38 38 from rhodecode.model.validation_schema.schemas import repo_schema
39 39
40 40 log = logging.getLogger(__name__)
41 41
42 42
43 43 class RepoSettingsView(RepoAppView):
44 44
45 45 def load_default_context(self):
46 46 c = self._get_local_tmpl_context()
47 47
48 48 acl_groups = RepoGroupList(
49 49 RepoGroup.query().all(),
50 50 perm_set=['group.write', 'group.admin'])
51 51 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
52 52 c.repo_groups_choices = map(lambda k: k[0], c.repo_groups)
53 53
54 54 # in case someone no longer have a group.write access to a repository
55 55 # pre fill the list with this entry, we don't care if this is the same
56 56 # but it will allow saving repo data properly.
57 57 repo_group = self.db_repo.group
58 58 if repo_group and repo_group.group_id not in c.repo_groups_choices:
59 59 c.repo_groups_choices.append(repo_group.group_id)
60 60 c.repo_groups.append(RepoGroup._generate_choice(repo_group))
61 61
62 62 if c.repository_requirements_missing or self.rhodecode_vcs_repo is None:
63 63 # we might be in missing requirement state, so we load things
64 64 # without touching scm_instance()
65 65 c.landing_revs_choices, c.landing_revs = \
66 66 ScmModel().get_repo_landing_revs(self.request.translate)
67 67 else:
68 68 c.landing_revs_choices, c.landing_revs = \
69 69 ScmModel().get_repo_landing_revs(
70 70 self.request.translate, self.db_repo)
71 71
72 72 c.personal_repo_group = c.auth_user.personal_repo_group
73 73 c.repo_fields = RepositoryField.query()\
74 74 .filter(RepositoryField.repository == self.db_repo).all()
75 75 return c
76 76
77 77 def _get_schema(self, c, old_values=None):
78 78 return repo_schema.RepoSettingsSchema().bind(
79 79 repo_type=self.db_repo.repo_type,
80 80 repo_type_options=[self.db_repo.repo_type],
81 81 repo_ref_options=c.landing_revs_choices,
82 82 repo_ref_items=c.landing_revs,
83 83 repo_repo_group_options=c.repo_groups_choices,
84 84 repo_repo_group_items=c.repo_groups,
85 85 # user caller
86 86 user=self._rhodecode_user,
87 87 old_values=old_values
88 88 )
89 89
90 90 @LoginRequired()
91 91 @HasRepoPermissionAnyDecorator('repository.admin')
92 92 @view_config(
93 93 route_name='edit_repo', request_method='GET',
94 94 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
95 95 def edit_settings(self):
96 96 c = self.load_default_context()
97 97 c.active = 'settings'
98 98
99 99 defaults = RepoModel()._get_defaults(self.db_repo_name)
100 100 defaults['repo_owner'] = defaults['user']
101 101 defaults['repo_landing_commit_ref'] = defaults['repo_landing_rev']
102 102
103 103 schema = self._get_schema(c)
104 104 c.form = RcForm(schema, appstruct=defaults)
105 105 return self._get_template_context(c)
106 106
107 107 @LoginRequired()
108 108 @HasRepoPermissionAnyDecorator('repository.admin')
109 109 @CSRFRequired()
110 110 @view_config(
111 111 route_name='edit_repo', request_method='POST',
112 112 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
113 113 def edit_settings_update(self):
114 114 _ = self.request.translate
115 115 c = self.load_default_context()
116 116 c.active = 'settings'
117 117 old_repo_name = self.db_repo_name
118 118
119 119 old_values = self.db_repo.get_api_data()
120 120 schema = self._get_schema(c, old_values=old_values)
121 121
122 122 c.form = RcForm(schema)
123 123 pstruct = self.request.POST.items()
124 124 pstruct.append(('repo_type', self.db_repo.repo_type))
125 125 try:
126 126 schema_data = c.form.validate(pstruct)
127 127 except deform.ValidationFailure as err_form:
128 128 return self._get_template_context(c)
129 129
130 130 # data is now VALID, proceed with updates
131 131 # save validated data back into the updates dict
132 132 validated_updates = dict(
133 133 repo_name=schema_data['repo_group']['repo_name_without_group'],
134 134 repo_group=schema_data['repo_group']['repo_group_id'],
135 135
136 136 user=schema_data['repo_owner'],
137 137 repo_description=schema_data['repo_description'],
138 138 repo_private=schema_data['repo_private'],
139 139 clone_uri=schema_data['repo_clone_uri'],
140 140 push_uri=schema_data['repo_push_uri'],
141 141 repo_landing_rev=schema_data['repo_landing_commit_ref'],
142 142 repo_enable_statistics=schema_data['repo_enable_statistics'],
143 143 repo_enable_locking=schema_data['repo_enable_locking'],
144 144 repo_enable_downloads=schema_data['repo_enable_downloads'],
145 145 )
146 146 # detect if SYNC URI changed, if we get OLD means we keep old values
147 147 if schema_data['repo_clone_uri_change'] == 'OLD':
148 148 validated_updates['clone_uri'] = self.db_repo.clone_uri
149 149
150 150 if schema_data['repo_push_uri_change'] == 'OLD':
151 151 validated_updates['push_uri'] = self.db_repo.push_uri
152 152
153 153 # use the new full name for redirect
154 154 new_repo_name = schema_data['repo_group']['repo_name_with_group']
155 155
156 156 # save extra fields into our validated data
157 157 for key, value in pstruct:
158 158 if key.startswith(RepositoryField.PREFIX):
159 159 validated_updates[key] = value
160 160
161 161 try:
162 162 RepoModel().update(self.db_repo, **validated_updates)
163 163 ScmModel().mark_for_invalidation(new_repo_name)
164 164
165 165 audit_logger.store_web(
166 166 'repo.edit', action_data={'old_data': old_values},
167 167 user=self._rhodecode_user, repo=self.db_repo)
168 168
169 169 Session().commit()
170 170
171 h.flash(_('Repository `{}` updated successfully').format(
172 old_repo_name), category='success')
171 h.flash(_('Repository `{}` updated successfully').format(old_repo_name),
172 category='success')
173 173 except Exception:
174 174 log.exception("Exception during update of repository")
175 175 h.flash(_('Error occurred during update of repository {}').format(
176 176 old_repo_name), category='error')
177 177
178 178 name_changed = old_repo_name != new_repo_name
179 179 if name_changed:
180 current_perms = self.db_repo.permissions(expand_from_user_groups=True)
181 affected_user_ids = [perm['user_id'] for perm in current_perms]
182
183 # NOTE(marcink): also add owner maybe it has changed
180 184 owner = User.get_by_username(schema_data['repo_owner'])
181 185 owner_id = owner.user_id if owner else self._rhodecode_user.user_id
182 events.trigger(events.UserPermissionsChange([
183 self._rhodecode_user.user_id, owner_id]))
186 affected_user_ids.extend([self._rhodecode_user.user_id, owner_id])
187 events.trigger(events.UserPermissionsChange(affected_user_ids))
184 188
185 189 raise HTTPFound(
186 190 h.route_path('edit_repo', repo_name=new_repo_name))
187 191
188 192 @LoginRequired()
189 193 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
190 194 @view_config(
191 195 route_name='repo_edit_toggle_locking', request_method='GET',
192 196 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
193 197 def toggle_locking(self):
194 198 """
195 199 Toggle locking of repository by simple GET call to url
196 200 """
197 201 _ = self.request.translate
198 202 repo = self.db_repo
199 203
200 204 try:
201 205 if repo.enable_locking:
202 206 if repo.locked[0]:
203 207 Repository.unlock(repo)
204 208 action = _('Unlocked')
205 209 else:
206 210 Repository.lock(
207 211 repo, self._rhodecode_user.user_id,
208 212 lock_reason=Repository.LOCK_WEB)
209 213 action = _('Locked')
210 214
211 215 h.flash(_('Repository has been %s') % action,
212 216 category='success')
213 217 except Exception:
214 218 log.exception("Exception during unlocking")
215 219 h.flash(_('An error occurred during unlocking'),
216 220 category='error')
217 221 raise HTTPFound(
218 222 h.route_path('repo_summary', repo_name=self.db_repo_name))
219 223
220 224 @LoginRequired()
221 225 @HasRepoPermissionAnyDecorator('repository.admin')
222 226 @view_config(
223 227 route_name='edit_repo_statistics', request_method='GET',
224 228 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
225 229 def edit_statistics_form(self):
226 230 c = self.load_default_context()
227 231
228 232 if self.db_repo.stats:
229 233 # this is on what revision we ended up so we add +1 for count
230 234 last_rev = self.db_repo.stats.stat_on_revision + 1
231 235 else:
232 236 last_rev = 0
233 237
234 238 c.active = 'statistics'
235 239 c.stats_revision = last_rev
236 240 c.repo_last_rev = self.rhodecode_vcs_repo.count()
237 241
238 242 if last_rev == 0 or c.repo_last_rev == 0:
239 243 c.stats_percentage = 0
240 244 else:
241 245 c.stats_percentage = '%.2f' % (
242 246 (float((last_rev)) / c.repo_last_rev) * 100)
243 247 return self._get_template_context(c)
244 248
245 249 @LoginRequired()
246 250 @HasRepoPermissionAnyDecorator('repository.admin')
247 251 @CSRFRequired()
248 252 @view_config(
249 253 route_name='edit_repo_statistics_reset', request_method='POST',
250 254 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
251 255 def repo_statistics_reset(self):
252 256 _ = self.request.translate
253 257
254 258 try:
255 259 RepoModel().delete_stats(self.db_repo_name)
256 260 Session().commit()
257 261 except Exception:
258 262 log.exception('Edit statistics failure')
259 263 h.flash(_('An error occurred during deletion of repository stats'),
260 264 category='error')
261 265 raise HTTPFound(
262 266 h.route_path('edit_repo_statistics', repo_name=self.db_repo_name))
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now