##// END OF EJS Templates
views: load template context for XHR views....
marcink -
r2308:323842c2 default
parent child Browse files
Show More
@@ -1,247 +1,248 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 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 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.response import Response
29 29 from pyramid.renderers import render
30 30
31 31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 32 from rhodecode.lib.auth import (
33 33 LoginRequired, NotAnonymous, CSRFRequired, HasPermissionAnyDecorator)
34 34 from rhodecode.lib import helpers as h, audit_logger
35 35 from rhodecode.lib.utils2 import safe_unicode
36 36
37 37 from rhodecode.model.forms import UserGroupForm
38 38 from rhodecode.model.permission import PermissionModel
39 39 from rhodecode.model.scm import UserGroupList
40 40 from rhodecode.model.db import (
41 41 or_, count, User, UserGroup, UserGroupMember)
42 42 from rhodecode.model.meta import Session
43 43 from rhodecode.model.user_group import UserGroupModel
44 44
45 45 log = logging.getLogger(__name__)
46 46
47 47
48 48 class AdminUserGroupsView(BaseAppView, DataGridAppView):
49 49
50 50 def load_default_context(self):
51 51 c = self._get_local_tmpl_context()
52 52
53 53 PermissionModel().set_global_permission_choices(
54 54 c, gettext_translator=self.request.translate)
55 55
56 56 self._register_global_c(c)
57 57 return c
58 58
59 59 # permission check in data loading of
60 60 # `user_groups_list_data` via UserGroupList
61 61 @LoginRequired()
62 62 @NotAnonymous()
63 63 @view_config(
64 64 route_name='user_groups', request_method='GET',
65 65 renderer='rhodecode:templates/admin/user_groups/user_groups.mako')
66 66 def user_groups_list(self):
67 67 c = self.load_default_context()
68 68 return self._get_template_context(c)
69 69
70 70 # permission check inside
71 71 @LoginRequired()
72 72 @NotAnonymous()
73 73 @view_config(
74 74 route_name='user_groups_data', request_method='GET',
75 75 renderer='json_ext', xhr=True)
76 76 def user_groups_list_data(self):
77 self.load_default_context()
77 78 column_map = {
78 79 'active': 'users_group_active',
79 80 'description': 'user_group_description',
80 81 'members': 'members_total',
81 82 'owner': 'user_username',
82 83 'sync': 'group_data'
83 84 }
84 85 draw, start, limit = self._extract_chunk(self.request)
85 86 search_q, order_by, order_dir = self._extract_ordering(
86 87 self.request, column_map=column_map)
87 88
88 89 _render = self.request.get_partial_renderer(
89 90 'data_table/_dt_elements.mako')
90 91
91 92 def user_group_name(user_group_id, user_group_name):
92 93 return _render("user_group_name", user_group_id, user_group_name)
93 94
94 95 def user_group_actions(user_group_id, user_group_name):
95 96 return _render("user_group_actions", user_group_id, user_group_name)
96 97
97 98 def user_profile(username):
98 99 return _render('user_profile', username)
99 100
100 101 auth_user_group_list = UserGroupList(
101 102 UserGroup.query().all(), perm_set=['usergroup.admin'])
102 103
103 104 allowed_ids = [-1]
104 105 for user_group in auth_user_group_list:
105 106 allowed_ids.append(user_group.users_group_id)
106 107
107 108 user_groups_data_total_count = UserGroup.query()\
108 109 .filter(UserGroup.users_group_id.in_(allowed_ids))\
109 110 .count()
110 111
111 112 member_count = count(UserGroupMember.user_id)
112 113 base_q = Session.query(
113 114 UserGroup.users_group_name,
114 115 UserGroup.user_group_description,
115 116 UserGroup.users_group_active,
116 117 UserGroup.users_group_id,
117 118 UserGroup.group_data,
118 119 User,
119 120 member_count.label('member_count')
120 121 ) \
121 122 .filter(UserGroup.users_group_id.in_(allowed_ids)) \
122 123 .outerjoin(UserGroupMember) \
123 124 .join(User, User.user_id == UserGroup.user_id) \
124 125 .group_by(UserGroup, User)
125 126
126 127 if search_q:
127 128 like_expression = u'%{}%'.format(safe_unicode(search_q))
128 129 base_q = base_q.filter(or_(
129 130 UserGroup.users_group_name.ilike(like_expression),
130 131 ))
131 132
132 133 user_groups_data_total_filtered_count = base_q.count()
133 134
134 135 if order_by == 'members_total':
135 136 sort_col = member_count
136 137 elif order_by == 'user_username':
137 138 sort_col = User.username
138 139 else:
139 140 sort_col = getattr(UserGroup, order_by, None)
140 141
141 142 if isinstance(sort_col, count) or sort_col:
142 143 if order_dir == 'asc':
143 144 sort_col = sort_col.asc()
144 145 else:
145 146 sort_col = sort_col.desc()
146 147
147 148 base_q = base_q.order_by(sort_col)
148 149 base_q = base_q.offset(start).limit(limit)
149 150
150 151 # authenticated access to user groups
151 152 auth_user_group_list = base_q.all()
152 153
153 154 user_groups_data = []
154 155 for user_gr in auth_user_group_list:
155 156 user_groups_data.append({
156 157 "users_group_name": user_group_name(
157 158 user_gr.users_group_id, h.escape(user_gr.users_group_name)),
158 159 "name_raw": h.escape(user_gr.users_group_name),
159 160 "description": h.escape(user_gr.user_group_description),
160 161 "members": user_gr.member_count,
161 162 # NOTE(marcink): because of advanced query we
162 163 # need to load it like that
163 164 "sync": UserGroup._load_group_data(
164 165 user_gr.group_data).get('extern_type'),
165 166 "active": h.bool2icon(user_gr.users_group_active),
166 167 "owner": user_profile(user_gr.User.username),
167 168 "action": user_group_actions(
168 169 user_gr.users_group_id, user_gr.users_group_name)
169 170 })
170 171
171 172 data = ({
172 173 'draw': draw,
173 174 'data': user_groups_data,
174 175 'recordsTotal': user_groups_data_total_count,
175 176 'recordsFiltered': user_groups_data_total_filtered_count,
176 177 })
177 178
178 179 return data
179 180
180 181 @LoginRequired()
181 182 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
182 183 @view_config(
183 184 route_name='user_groups_new', request_method='GET',
184 185 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
185 186 def user_groups_new(self):
186 187 c = self.load_default_context()
187 188 return self._get_template_context(c)
188 189
189 190 @LoginRequired()
190 191 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
191 192 @CSRFRequired()
192 193 @view_config(
193 194 route_name='user_groups_create', request_method='POST',
194 195 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
195 196 def user_groups_create(self):
196 197 _ = self.request.translate
197 198 c = self.load_default_context()
198 199 users_group_form = UserGroupForm()()
199 200
200 201 user_group_name = self.request.POST.get('users_group_name')
201 202 try:
202 203 form_result = users_group_form.to_python(dict(self.request.POST))
203 204 user_group = UserGroupModel().create(
204 205 name=form_result['users_group_name'],
205 206 description=form_result['user_group_description'],
206 207 owner=self._rhodecode_user.user_id,
207 208 active=form_result['users_group_active'])
208 209 Session().flush()
209 210 creation_data = user_group.get_api_data()
210 211 user_group_name = form_result['users_group_name']
211 212
212 213 audit_logger.store_web(
213 214 'user_group.create', action_data={'data': creation_data},
214 215 user=self._rhodecode_user)
215 216
216 217 user_group_link = h.link_to(
217 218 h.escape(user_group_name),
218 219 h.route_path(
219 220 'edit_user_group', user_group_id=user_group.users_group_id))
220 221 h.flash(h.literal(_('Created user group %(user_group_link)s')
221 222 % {'user_group_link': user_group_link}),
222 223 category='success')
223 224 Session().commit()
224 225 user_group_id = user_group.users_group_id
225 226 except formencode.Invalid as errors:
226 227
227 228 data = render(
228 229 'rhodecode:templates/admin/user_groups/user_group_add.mako',
229 230 self._get_template_context(c), self.request)
230 231 html = formencode.htmlfill.render(
231 232 data,
232 233 defaults=errors.value,
233 234 errors=errors.error_dict or {},
234 235 prefix_error=False,
235 236 encoding="UTF-8",
236 237 force_defaults=False
237 238 )
238 239 return Response(html)
239 240
240 241 except Exception:
241 242 log.exception("Exception creating user group")
242 243 h.flash(_('Error occurred during creation of user group %s') \
243 244 % user_group_name, category='error')
244 245 raise HTTPFound(h.route_path('user_groups_new'))
245 246
246 247 raise HTTPFound(
247 248 h.route_path('edit_user_group', user_group_id=user_group_id))
@@ -1,1178 +1,1179 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 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.apps._base import BaseAppView, DataGridAppView, UserAppView
32 32 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
33 33 from rhodecode.authentication.plugins import auth_rhodecode
34 34 from rhodecode.events import trigger
35 35
36 36 from rhodecode.lib import audit_logger
37 37 from rhodecode.lib.exceptions import (
38 38 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
39 39 UserOwnsUserGroupsException, DefaultUserException)
40 40 from rhodecode.lib.ext_json import json
41 41 from rhodecode.lib.auth import (
42 42 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
43 43 from rhodecode.lib import helpers as h
44 44 from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict
45 45 from rhodecode.model.auth_token import AuthTokenModel
46 46 from rhodecode.model.forms import (
47 47 UserForm, UserIndividualPermissionsForm, UserPermissionsForm)
48 48 from rhodecode.model.permission import PermissionModel
49 49 from rhodecode.model.repo_group import RepoGroupModel
50 50 from rhodecode.model.ssh_key import SshKeyModel
51 51 from rhodecode.model.user import UserModel
52 52 from rhodecode.model.user_group import UserGroupModel
53 53 from rhodecode.model.db import (
54 54 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
55 55 UserApiKeys, UserSshKeys, RepoGroup)
56 56 from rhodecode.model.meta import Session
57 57
58 58 log = logging.getLogger(__name__)
59 59
60 60
61 61 class AdminUsersView(BaseAppView, DataGridAppView):
62 62
63 63 def load_default_context(self):
64 64 c = self._get_local_tmpl_context()
65 65 self._register_global_c(c)
66 66 return c
67 67
68 68 @LoginRequired()
69 69 @HasPermissionAllDecorator('hg.admin')
70 70 @view_config(
71 71 route_name='users', request_method='GET',
72 72 renderer='rhodecode:templates/admin/users/users.mako')
73 73 def users_list(self):
74 74 c = self.load_default_context()
75 75 return self._get_template_context(c)
76 76
77 77 @LoginRequired()
78 78 @HasPermissionAllDecorator('hg.admin')
79 79 @view_config(
80 80 # renderer defined below
81 81 route_name='users_data', request_method='GET',
82 82 renderer='json_ext', xhr=True)
83 83 def users_list_data(self):
84 self.load_default_context()
84 85 column_map = {
85 86 'first_name': 'name',
86 87 'last_name': 'lastname',
87 88 }
88 89 draw, start, limit = self._extract_chunk(self.request)
89 90 search_q, order_by, order_dir = self._extract_ordering(
90 91 self.request, column_map=column_map)
91 92
92 93 _render = self.request.get_partial_renderer(
93 94 'data_table/_dt_elements.mako')
94 95
95 96 def user_actions(user_id, username):
96 97 return _render("user_actions", user_id, username)
97 98
98 99 users_data_total_count = User.query()\
99 100 .filter(User.username != User.DEFAULT_USER) \
100 101 .count()
101 102
102 103 # json generate
103 104 base_q = User.query().filter(User.username != User.DEFAULT_USER)
104 105
105 106 if search_q:
106 107 like_expression = u'%{}%'.format(safe_unicode(search_q))
107 108 base_q = base_q.filter(or_(
108 109 User.username.ilike(like_expression),
109 110 User._email.ilike(like_expression),
110 111 User.name.ilike(like_expression),
111 112 User.lastname.ilike(like_expression),
112 113 ))
113 114
114 115 users_data_total_filtered_count = base_q.count()
115 116
116 117 sort_col = getattr(User, order_by, None)
117 118 if sort_col:
118 119 if order_dir == 'asc':
119 120 # handle null values properly to order by NULL last
120 121 if order_by in ['last_activity']:
121 122 sort_col = coalesce(sort_col, datetime.date.max)
122 123 sort_col = sort_col.asc()
123 124 else:
124 125 # handle null values properly to order by NULL last
125 126 if order_by in ['last_activity']:
126 127 sort_col = coalesce(sort_col, datetime.date.min)
127 128 sort_col = sort_col.desc()
128 129
129 130 base_q = base_q.order_by(sort_col)
130 131 base_q = base_q.offset(start).limit(limit)
131 132
132 133 users_list = base_q.all()
133 134
134 135 users_data = []
135 136 for user in users_list:
136 137 users_data.append({
137 138 "username": h.gravatar_with_user(self.request, user.username),
138 139 "email": user.email,
139 140 "first_name": user.first_name,
140 141 "last_name": user.last_name,
141 142 "last_login": h.format_date(user.last_login),
142 143 "last_activity": h.format_date(user.last_activity),
143 144 "active": h.bool2icon(user.active),
144 145 "active_raw": user.active,
145 146 "admin": h.bool2icon(user.admin),
146 147 "extern_type": user.extern_type,
147 148 "extern_name": user.extern_name,
148 149 "action": user_actions(user.user_id, user.username),
149 150 })
150 151
151 152 data = ({
152 153 'draw': draw,
153 154 'data': users_data,
154 155 'recordsTotal': users_data_total_count,
155 156 'recordsFiltered': users_data_total_filtered_count,
156 157 })
157 158
158 159 return data
159 160
160 161 def _set_personal_repo_group_template_vars(self, c_obj):
161 162 DummyUser = AttributeDict({
162 163 'username': '${username}',
163 164 'user_id': '${user_id}',
164 165 })
165 166 c_obj.default_create_repo_group = RepoGroupModel() \
166 167 .get_default_create_personal_repo_group()
167 168 c_obj.personal_repo_group_name = RepoGroupModel() \
168 169 .get_personal_group_name(DummyUser)
169 170
170 171 @LoginRequired()
171 172 @HasPermissionAllDecorator('hg.admin')
172 173 @view_config(
173 174 route_name='users_new', request_method='GET',
174 175 renderer='rhodecode:templates/admin/users/user_add.mako')
175 176 def users_new(self):
176 177 _ = self.request.translate
177 178 c = self.load_default_context()
178 179 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
179 180 self._set_personal_repo_group_template_vars(c)
180 181 return self._get_template_context(c)
181 182
182 183 @LoginRequired()
183 184 @HasPermissionAllDecorator('hg.admin')
184 185 @CSRFRequired()
185 186 @view_config(
186 187 route_name='users_create', request_method='POST',
187 188 renderer='rhodecode:templates/admin/users/user_add.mako')
188 189 def users_create(self):
189 190 _ = self.request.translate
190 191 c = self.load_default_context()
191 192 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
192 193 user_model = UserModel()
193 194 user_form = UserForm()()
194 195 try:
195 196 form_result = user_form.to_python(dict(self.request.POST))
196 197 user = user_model.create(form_result)
197 198 Session().flush()
198 199 creation_data = user.get_api_data()
199 200 username = form_result['username']
200 201
201 202 audit_logger.store_web(
202 203 'user.create', action_data={'data': creation_data},
203 204 user=c.rhodecode_user)
204 205
205 206 user_link = h.link_to(
206 207 h.escape(username),
207 208 h.route_path('user_edit', user_id=user.user_id))
208 209 h.flash(h.literal(_('Created user %(user_link)s')
209 210 % {'user_link': user_link}), category='success')
210 211 Session().commit()
211 212 except formencode.Invalid as errors:
212 213 self._set_personal_repo_group_template_vars(c)
213 214 data = render(
214 215 'rhodecode:templates/admin/users/user_add.mako',
215 216 self._get_template_context(c), self.request)
216 217 html = formencode.htmlfill.render(
217 218 data,
218 219 defaults=errors.value,
219 220 errors=errors.error_dict or {},
220 221 prefix_error=False,
221 222 encoding="UTF-8",
222 223 force_defaults=False
223 224 )
224 225 return Response(html)
225 226 except UserCreationError as e:
226 227 h.flash(e, 'error')
227 228 except Exception:
228 229 log.exception("Exception creation of user")
229 230 h.flash(_('Error occurred during creation of user %s')
230 231 % self.request.POST.get('username'), category='error')
231 232 raise HTTPFound(h.route_path('users'))
232 233
233 234
234 235 class UsersView(UserAppView):
235 236 ALLOW_SCOPED_TOKENS = False
236 237 """
237 238 This view has alternative version inside EE, if modified please take a look
238 239 in there as well.
239 240 """
240 241
241 242 def load_default_context(self):
242 243 c = self._get_local_tmpl_context()
243 244 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
244 245 c.allowed_languages = [
245 246 ('en', 'English (en)'),
246 247 ('de', 'German (de)'),
247 248 ('fr', 'French (fr)'),
248 249 ('it', 'Italian (it)'),
249 250 ('ja', 'Japanese (ja)'),
250 251 ('pl', 'Polish (pl)'),
251 252 ('pt', 'Portuguese (pt)'),
252 253 ('ru', 'Russian (ru)'),
253 254 ('zh', 'Chinese (zh)'),
254 255 ]
255 256 req = self.request
256 257
257 258 c.available_permissions = req.registry.settings['available_permissions']
258 259 PermissionModel().set_global_permission_choices(
259 260 c, gettext_translator=req.translate)
260 261
261 262 self._register_global_c(c)
262 263 return c
263 264
264 265 @LoginRequired()
265 266 @HasPermissionAllDecorator('hg.admin')
266 267 @CSRFRequired()
267 268 @view_config(
268 269 route_name='user_update', request_method='POST',
269 270 renderer='rhodecode:templates/admin/users/user_edit.mako')
270 271 def user_update(self):
271 272 _ = self.request.translate
272 273 c = self.load_default_context()
273 274
274 275 user_id = self.db_user_id
275 276 c.user = self.db_user
276 277
277 278 c.active = 'profile'
278 279 c.extern_type = c.user.extern_type
279 280 c.extern_name = c.user.extern_name
280 281 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
281 282 available_languages = [x[0] for x in c.allowed_languages]
282 283 _form = UserForm(edit=True, available_languages=available_languages,
283 284 old_data={'user_id': user_id,
284 285 'email': c.user.email})()
285 286 form_result = {}
286 287 old_values = c.user.get_api_data()
287 288 try:
288 289 form_result = _form.to_python(dict(self.request.POST))
289 290 skip_attrs = ['extern_type', 'extern_name']
290 291 # TODO: plugin should define if username can be updated
291 292 if c.extern_type != "rhodecode":
292 293 # forbid updating username for external accounts
293 294 skip_attrs.append('username')
294 295
295 296 UserModel().update_user(
296 297 user_id, skip_attrs=skip_attrs, **form_result)
297 298
298 299 audit_logger.store_web(
299 300 'user.edit', action_data={'old_data': old_values},
300 301 user=c.rhodecode_user)
301 302
302 303 Session().commit()
303 304 h.flash(_('User updated successfully'), category='success')
304 305 except formencode.Invalid as errors:
305 306 data = render(
306 307 'rhodecode:templates/admin/users/user_edit.mako',
307 308 self._get_template_context(c), self.request)
308 309 html = formencode.htmlfill.render(
309 310 data,
310 311 defaults=errors.value,
311 312 errors=errors.error_dict or {},
312 313 prefix_error=False,
313 314 encoding="UTF-8",
314 315 force_defaults=False
315 316 )
316 317 return Response(html)
317 318 except UserCreationError as e:
318 319 h.flash(e, 'error')
319 320 except Exception:
320 321 log.exception("Exception updating user")
321 322 h.flash(_('Error occurred during update of user %s')
322 323 % form_result.get('username'), category='error')
323 324 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
324 325
325 326 @LoginRequired()
326 327 @HasPermissionAllDecorator('hg.admin')
327 328 @CSRFRequired()
328 329 @view_config(
329 330 route_name='user_delete', request_method='POST',
330 331 renderer='rhodecode:templates/admin/users/user_edit.mako')
331 332 def user_delete(self):
332 333 _ = self.request.translate
333 334 c = self.load_default_context()
334 335 c.user = self.db_user
335 336
336 337 _repos = c.user.repositories
337 338 _repo_groups = c.user.repository_groups
338 339 _user_groups = c.user.user_groups
339 340
340 341 handle_repos = None
341 342 handle_repo_groups = None
342 343 handle_user_groups = None
343 344 # dummy call for flash of handle
344 345 set_handle_flash_repos = lambda: None
345 346 set_handle_flash_repo_groups = lambda: None
346 347 set_handle_flash_user_groups = lambda: None
347 348
348 349 if _repos and self.request.POST.get('user_repos'):
349 350 do = self.request.POST['user_repos']
350 351 if do == 'detach':
351 352 handle_repos = 'detach'
352 353 set_handle_flash_repos = lambda: h.flash(
353 354 _('Detached %s repositories') % len(_repos),
354 355 category='success')
355 356 elif do == 'delete':
356 357 handle_repos = 'delete'
357 358 set_handle_flash_repos = lambda: h.flash(
358 359 _('Deleted %s repositories') % len(_repos),
359 360 category='success')
360 361
361 362 if _repo_groups and self.request.POST.get('user_repo_groups'):
362 363 do = self.request.POST['user_repo_groups']
363 364 if do == 'detach':
364 365 handle_repo_groups = 'detach'
365 366 set_handle_flash_repo_groups = lambda: h.flash(
366 367 _('Detached %s repository groups') % len(_repo_groups),
367 368 category='success')
368 369 elif do == 'delete':
369 370 handle_repo_groups = 'delete'
370 371 set_handle_flash_repo_groups = lambda: h.flash(
371 372 _('Deleted %s repository groups') % len(_repo_groups),
372 373 category='success')
373 374
374 375 if _user_groups and self.request.POST.get('user_user_groups'):
375 376 do = self.request.POST['user_user_groups']
376 377 if do == 'detach':
377 378 handle_user_groups = 'detach'
378 379 set_handle_flash_user_groups = lambda: h.flash(
379 380 _('Detached %s user groups') % len(_user_groups),
380 381 category='success')
381 382 elif do == 'delete':
382 383 handle_user_groups = 'delete'
383 384 set_handle_flash_user_groups = lambda: h.flash(
384 385 _('Deleted %s user groups') % len(_user_groups),
385 386 category='success')
386 387
387 388 old_values = c.user.get_api_data()
388 389 try:
389 390 UserModel().delete(c.user, handle_repos=handle_repos,
390 391 handle_repo_groups=handle_repo_groups,
391 392 handle_user_groups=handle_user_groups)
392 393
393 394 audit_logger.store_web(
394 395 'user.delete', action_data={'old_data': old_values},
395 396 user=c.rhodecode_user)
396 397
397 398 Session().commit()
398 399 set_handle_flash_repos()
399 400 set_handle_flash_repo_groups()
400 401 set_handle_flash_user_groups()
401 402 h.flash(_('Successfully deleted user'), category='success')
402 403 except (UserOwnsReposException, UserOwnsRepoGroupsException,
403 404 UserOwnsUserGroupsException, DefaultUserException) as e:
404 405 h.flash(e, category='warning')
405 406 except Exception:
406 407 log.exception("Exception during deletion of user")
407 408 h.flash(_('An error occurred during deletion of user'),
408 409 category='error')
409 410 raise HTTPFound(h.route_path('users'))
410 411
411 412 @LoginRequired()
412 413 @HasPermissionAllDecorator('hg.admin')
413 414 @view_config(
414 415 route_name='user_edit', request_method='GET',
415 416 renderer='rhodecode:templates/admin/users/user_edit.mako')
416 417 def user_edit(self):
417 418 _ = self.request.translate
418 419 c = self.load_default_context()
419 420 c.user = self.db_user
420 421
421 422 c.active = 'profile'
422 423 c.extern_type = c.user.extern_type
423 424 c.extern_name = c.user.extern_name
424 425 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
425 426
426 427 defaults = c.user.get_dict()
427 428 defaults.update({'language': c.user.user_data.get('language')})
428 429
429 430 data = render(
430 431 'rhodecode:templates/admin/users/user_edit.mako',
431 432 self._get_template_context(c), self.request)
432 433 html = formencode.htmlfill.render(
433 434 data,
434 435 defaults=defaults,
435 436 encoding="UTF-8",
436 437 force_defaults=False
437 438 )
438 439 return Response(html)
439 440
440 441 @LoginRequired()
441 442 @HasPermissionAllDecorator('hg.admin')
442 443 @view_config(
443 444 route_name='user_edit_advanced', request_method='GET',
444 445 renderer='rhodecode:templates/admin/users/user_edit.mako')
445 446 def user_edit_advanced(self):
446 447 _ = self.request.translate
447 448 c = self.load_default_context()
448 449
449 450 user_id = self.db_user_id
450 451 c.user = self.db_user
451 452
452 453 c.active = 'advanced'
453 454 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
454 455 c.personal_repo_group_name = RepoGroupModel()\
455 456 .get_personal_group_name(c.user)
456 457
457 458 c.user_to_review_rules = sorted(
458 459 (x.user for x in c.user.user_review_rules),
459 460 key=lambda u: u.username.lower())
460 461
461 462 c.first_admin = User.get_first_super_admin()
462 463 defaults = c.user.get_dict()
463 464
464 465 # Interim workaround if the user participated on any pull requests as a
465 466 # reviewer.
466 467 has_review = len(c.user.reviewer_pull_requests)
467 468 c.can_delete_user = not has_review
468 469 c.can_delete_user_message = ''
469 470 inactive_link = h.link_to(
470 471 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
471 472 if has_review == 1:
472 473 c.can_delete_user_message = h.literal(_(
473 474 'The user participates as reviewer in {} pull request and '
474 475 'cannot be deleted. \nYou can set the user to '
475 476 '"{}" instead of deleting it.').format(
476 477 has_review, inactive_link))
477 478 elif has_review:
478 479 c.can_delete_user_message = h.literal(_(
479 480 'The user participates as reviewer in {} pull requests and '
480 481 'cannot be deleted. \nYou can set the user to '
481 482 '"{}" instead of deleting it.').format(
482 483 has_review, inactive_link))
483 484
484 485 data = render(
485 486 'rhodecode:templates/admin/users/user_edit.mako',
486 487 self._get_template_context(c), self.request)
487 488 html = formencode.htmlfill.render(
488 489 data,
489 490 defaults=defaults,
490 491 encoding="UTF-8",
491 492 force_defaults=False
492 493 )
493 494 return Response(html)
494 495
495 496 @LoginRequired()
496 497 @HasPermissionAllDecorator('hg.admin')
497 498 @view_config(
498 499 route_name='user_edit_global_perms', request_method='GET',
499 500 renderer='rhodecode:templates/admin/users/user_edit.mako')
500 501 def user_edit_global_perms(self):
501 502 _ = self.request.translate
502 503 c = self.load_default_context()
503 504 c.user = self.db_user
504 505
505 506 c.active = 'global_perms'
506 507
507 508 c.default_user = User.get_default_user()
508 509 defaults = c.user.get_dict()
509 510 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
510 511 defaults.update(c.default_user.get_default_perms())
511 512 defaults.update(c.user.get_default_perms())
512 513
513 514 data = render(
514 515 'rhodecode:templates/admin/users/user_edit.mako',
515 516 self._get_template_context(c), self.request)
516 517 html = formencode.htmlfill.render(
517 518 data,
518 519 defaults=defaults,
519 520 encoding="UTF-8",
520 521 force_defaults=False
521 522 )
522 523 return Response(html)
523 524
524 525 @LoginRequired()
525 526 @HasPermissionAllDecorator('hg.admin')
526 527 @CSRFRequired()
527 528 @view_config(
528 529 route_name='user_edit_global_perms_update', request_method='POST',
529 530 renderer='rhodecode:templates/admin/users/user_edit.mako')
530 531 def user_edit_global_perms_update(self):
531 532 _ = self.request.translate
532 533 c = self.load_default_context()
533 534
534 535 user_id = self.db_user_id
535 536 c.user = self.db_user
536 537
537 538 c.active = 'global_perms'
538 539 try:
539 540 # first stage that verifies the checkbox
540 541 _form = UserIndividualPermissionsForm()
541 542 form_result = _form.to_python(dict(self.request.POST))
542 543 inherit_perms = form_result['inherit_default_permissions']
543 544 c.user.inherit_default_permissions = inherit_perms
544 545 Session().add(c.user)
545 546
546 547 if not inherit_perms:
547 548 # only update the individual ones if we un check the flag
548 549 _form = UserPermissionsForm(
549 550 [x[0] for x in c.repo_create_choices],
550 551 [x[0] for x in c.repo_create_on_write_choices],
551 552 [x[0] for x in c.repo_group_create_choices],
552 553 [x[0] for x in c.user_group_create_choices],
553 554 [x[0] for x in c.fork_choices],
554 555 [x[0] for x in c.inherit_default_permission_choices])()
555 556
556 557 form_result = _form.to_python(dict(self.request.POST))
557 558 form_result.update({'perm_user_id': c.user.user_id})
558 559
559 560 PermissionModel().update_user_permissions(form_result)
560 561
561 562 # TODO(marcink): implement global permissions
562 563 # audit_log.store_web('user.edit.permissions')
563 564
564 565 Session().commit()
565 566 h.flash(_('User global permissions updated successfully'),
566 567 category='success')
567 568
568 569 except formencode.Invalid as errors:
569 570 data = render(
570 571 'rhodecode:templates/admin/users/user_edit.mako',
571 572 self._get_template_context(c), self.request)
572 573 html = formencode.htmlfill.render(
573 574 data,
574 575 defaults=errors.value,
575 576 errors=errors.error_dict or {},
576 577 prefix_error=False,
577 578 encoding="UTF-8",
578 579 force_defaults=False
579 580 )
580 581 return Response(html)
581 582 except Exception:
582 583 log.exception("Exception during permissions saving")
583 584 h.flash(_('An error occurred during permissions saving'),
584 585 category='error')
585 586 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
586 587
587 588 @LoginRequired()
588 589 @HasPermissionAllDecorator('hg.admin')
589 590 @CSRFRequired()
590 591 @view_config(
591 592 route_name='user_force_password_reset', request_method='POST',
592 593 renderer='rhodecode:templates/admin/users/user_edit.mako')
593 594 def user_force_password_reset(self):
594 595 """
595 596 toggle reset password flag for this user
596 597 """
597 598 _ = self.request.translate
598 599 c = self.load_default_context()
599 600
600 601 user_id = self.db_user_id
601 602 c.user = self.db_user
602 603
603 604 try:
604 605 old_value = c.user.user_data.get('force_password_change')
605 606 c.user.update_userdata(force_password_change=not old_value)
606 607
607 608 if old_value:
608 609 msg = _('Force password change disabled for user')
609 610 audit_logger.store_web(
610 611 'user.edit.password_reset.disabled',
611 612 user=c.rhodecode_user)
612 613 else:
613 614 msg = _('Force password change enabled for user')
614 615 audit_logger.store_web(
615 616 'user.edit.password_reset.enabled',
616 617 user=c.rhodecode_user)
617 618
618 619 Session().commit()
619 620 h.flash(msg, category='success')
620 621 except Exception:
621 622 log.exception("Exception during password reset for user")
622 623 h.flash(_('An error occurred during password reset for user'),
623 624 category='error')
624 625
625 626 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
626 627
627 628 @LoginRequired()
628 629 @HasPermissionAllDecorator('hg.admin')
629 630 @CSRFRequired()
630 631 @view_config(
631 632 route_name='user_create_personal_repo_group', request_method='POST',
632 633 renderer='rhodecode:templates/admin/users/user_edit.mako')
633 634 def user_create_personal_repo_group(self):
634 635 """
635 636 Create personal repository group for this user
636 637 """
637 638 from rhodecode.model.repo_group import RepoGroupModel
638 639
639 640 _ = self.request.translate
640 641 c = self.load_default_context()
641 642
642 643 user_id = self.db_user_id
643 644 c.user = self.db_user
644 645
645 646 personal_repo_group = RepoGroup.get_user_personal_repo_group(
646 647 c.user.user_id)
647 648 if personal_repo_group:
648 649 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
649 650
650 651 personal_repo_group_name = RepoGroupModel().get_personal_group_name(
651 652 c.user)
652 653 named_personal_group = RepoGroup.get_by_group_name(
653 654 personal_repo_group_name)
654 655 try:
655 656
656 657 if named_personal_group and named_personal_group.user_id == c.user.user_id:
657 658 # migrate the same named group, and mark it as personal
658 659 named_personal_group.personal = True
659 660 Session().add(named_personal_group)
660 661 Session().commit()
661 662 msg = _('Linked repository group `%s` as personal' % (
662 663 personal_repo_group_name,))
663 664 h.flash(msg, category='success')
664 665 elif not named_personal_group:
665 666 RepoGroupModel().create_personal_repo_group(c.user)
666 667
667 668 msg = _('Created repository group `%s`' % (
668 669 personal_repo_group_name,))
669 670 h.flash(msg, category='success')
670 671 else:
671 672 msg = _('Repository group `%s` is already taken' % (
672 673 personal_repo_group_name,))
673 674 h.flash(msg, category='warning')
674 675 except Exception:
675 676 log.exception("Exception during repository group creation")
676 677 msg = _(
677 678 'An error occurred during repository group creation for user')
678 679 h.flash(msg, category='error')
679 680 Session().rollback()
680 681
681 682 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
682 683
683 684 @LoginRequired()
684 685 @HasPermissionAllDecorator('hg.admin')
685 686 @view_config(
686 687 route_name='edit_user_auth_tokens', request_method='GET',
687 688 renderer='rhodecode:templates/admin/users/user_edit.mako')
688 689 def auth_tokens(self):
689 690 _ = self.request.translate
690 691 c = self.load_default_context()
691 692 c.user = self.db_user
692 693
693 694 c.active = 'auth_tokens'
694 695
695 696 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
696 697 c.role_values = [
697 698 (x, AuthTokenModel.cls._get_role_name(x))
698 699 for x in AuthTokenModel.cls.ROLES]
699 700 c.role_options = [(c.role_values, _("Role"))]
700 701 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
701 702 c.user.user_id, show_expired=True)
702 703 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
703 704 return self._get_template_context(c)
704 705
705 706 def maybe_attach_token_scope(self, token):
706 707 # implemented in EE edition
707 708 pass
708 709
709 710 @LoginRequired()
710 711 @HasPermissionAllDecorator('hg.admin')
711 712 @CSRFRequired()
712 713 @view_config(
713 714 route_name='edit_user_auth_tokens_add', request_method='POST')
714 715 def auth_tokens_add(self):
715 716 _ = self.request.translate
716 717 c = self.load_default_context()
717 718
718 719 user_id = self.db_user_id
719 720 c.user = self.db_user
720 721
721 722 user_data = c.user.get_api_data()
722 723 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
723 724 description = self.request.POST.get('description')
724 725 role = self.request.POST.get('role')
725 726
726 727 token = AuthTokenModel().create(
727 728 c.user.user_id, description, lifetime, role)
728 729 token_data = token.get_api_data()
729 730
730 731 self.maybe_attach_token_scope(token)
731 732 audit_logger.store_web(
732 733 'user.edit.token.add', action_data={
733 734 'data': {'token': token_data, 'user': user_data}},
734 735 user=self._rhodecode_user, )
735 736 Session().commit()
736 737
737 738 h.flash(_("Auth token successfully created"), category='success')
738 739 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
739 740
740 741 @LoginRequired()
741 742 @HasPermissionAllDecorator('hg.admin')
742 743 @CSRFRequired()
743 744 @view_config(
744 745 route_name='edit_user_auth_tokens_delete', request_method='POST')
745 746 def auth_tokens_delete(self):
746 747 _ = self.request.translate
747 748 c = self.load_default_context()
748 749
749 750 user_id = self.db_user_id
750 751 c.user = self.db_user
751 752
752 753 user_data = c.user.get_api_data()
753 754
754 755 del_auth_token = self.request.POST.get('del_auth_token')
755 756
756 757 if del_auth_token:
757 758 token = UserApiKeys.get_or_404(del_auth_token)
758 759 token_data = token.get_api_data()
759 760
760 761 AuthTokenModel().delete(del_auth_token, c.user.user_id)
761 762 audit_logger.store_web(
762 763 'user.edit.token.delete', action_data={
763 764 'data': {'token': token_data, 'user': user_data}},
764 765 user=self._rhodecode_user,)
765 766 Session().commit()
766 767 h.flash(_("Auth token successfully deleted"), category='success')
767 768
768 769 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
769 770
770 771 @LoginRequired()
771 772 @HasPermissionAllDecorator('hg.admin')
772 773 @view_config(
773 774 route_name='edit_user_ssh_keys', request_method='GET',
774 775 renderer='rhodecode:templates/admin/users/user_edit.mako')
775 776 def ssh_keys(self):
776 777 _ = self.request.translate
777 778 c = self.load_default_context()
778 779 c.user = self.db_user
779 780
780 781 c.active = 'ssh_keys'
781 782 c.default_key = self.request.GET.get('default_key')
782 783 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
783 784 return self._get_template_context(c)
784 785
785 786 @LoginRequired()
786 787 @HasPermissionAllDecorator('hg.admin')
787 788 @view_config(
788 789 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
789 790 renderer='rhodecode:templates/admin/users/user_edit.mako')
790 791 def ssh_keys_generate_keypair(self):
791 792 _ = self.request.translate
792 793 c = self.load_default_context()
793 794
794 795 c.user = self.db_user
795 796
796 797 c.active = 'ssh_keys_generate'
797 798 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
798 799 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
799 800
800 801 return self._get_template_context(c)
801 802
802 803 @LoginRequired()
803 804 @HasPermissionAllDecorator('hg.admin')
804 805 @CSRFRequired()
805 806 @view_config(
806 807 route_name='edit_user_ssh_keys_add', request_method='POST')
807 808 def ssh_keys_add(self):
808 809 _ = self.request.translate
809 810 c = self.load_default_context()
810 811
811 812 user_id = self.db_user_id
812 813 c.user = self.db_user
813 814
814 815 user_data = c.user.get_api_data()
815 816 key_data = self.request.POST.get('key_data')
816 817 description = self.request.POST.get('description')
817 818
818 819 try:
819 820 if not key_data:
820 821 raise ValueError('Please add a valid public key')
821 822
822 823 key = SshKeyModel().parse_key(key_data.strip())
823 824 fingerprint = key.hash_md5()
824 825
825 826 ssh_key = SshKeyModel().create(
826 827 c.user.user_id, fingerprint, key_data, description)
827 828 ssh_key_data = ssh_key.get_api_data()
828 829
829 830 audit_logger.store_web(
830 831 'user.edit.ssh_key.add', action_data={
831 832 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
832 833 user=self._rhodecode_user, )
833 834 Session().commit()
834 835
835 836 # Trigger an event on change of keys.
836 837 trigger(SshKeyFileChangeEvent(), self.request.registry)
837 838
838 839 h.flash(_("Ssh Key successfully created"), category='success')
839 840
840 841 except IntegrityError:
841 842 log.exception("Exception during ssh key saving")
842 843 h.flash(_('An error occurred during ssh key saving: {}').format(
843 844 'Such key already exists, please use a different one'),
844 845 category='error')
845 846 except Exception as e:
846 847 log.exception("Exception during ssh key saving")
847 848 h.flash(_('An error occurred during ssh key saving: {}').format(e),
848 849 category='error')
849 850
850 851 return HTTPFound(
851 852 h.route_path('edit_user_ssh_keys', user_id=user_id))
852 853
853 854 @LoginRequired()
854 855 @HasPermissionAllDecorator('hg.admin')
855 856 @CSRFRequired()
856 857 @view_config(
857 858 route_name='edit_user_ssh_keys_delete', request_method='POST')
858 859 def ssh_keys_delete(self):
859 860 _ = self.request.translate
860 861 c = self.load_default_context()
861 862
862 863 user_id = self.db_user_id
863 864 c.user = self.db_user
864 865
865 866 user_data = c.user.get_api_data()
866 867
867 868 del_ssh_key = self.request.POST.get('del_ssh_key')
868 869
869 870 if del_ssh_key:
870 871 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
871 872 ssh_key_data = ssh_key.get_api_data()
872 873
873 874 SshKeyModel().delete(del_ssh_key, c.user.user_id)
874 875 audit_logger.store_web(
875 876 'user.edit.ssh_key.delete', action_data={
876 877 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
877 878 user=self._rhodecode_user,)
878 879 Session().commit()
879 880 # Trigger an event on change of keys.
880 881 trigger(SshKeyFileChangeEvent(), self.request.registry)
881 882 h.flash(_("Ssh key successfully deleted"), category='success')
882 883
883 884 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
884 885
885 886 @LoginRequired()
886 887 @HasPermissionAllDecorator('hg.admin')
887 888 @view_config(
888 889 route_name='edit_user_emails', request_method='GET',
889 890 renderer='rhodecode:templates/admin/users/user_edit.mako')
890 891 def emails(self):
891 892 _ = self.request.translate
892 893 c = self.load_default_context()
893 894 c.user = self.db_user
894 895
895 896 c.active = 'emails'
896 897 c.user_email_map = UserEmailMap.query() \
897 898 .filter(UserEmailMap.user == c.user).all()
898 899
899 900 return self._get_template_context(c)
900 901
901 902 @LoginRequired()
902 903 @HasPermissionAllDecorator('hg.admin')
903 904 @CSRFRequired()
904 905 @view_config(
905 906 route_name='edit_user_emails_add', request_method='POST')
906 907 def emails_add(self):
907 908 _ = self.request.translate
908 909 c = self.load_default_context()
909 910
910 911 user_id = self.db_user_id
911 912 c.user = self.db_user
912 913
913 914 email = self.request.POST.get('new_email')
914 915 user_data = c.user.get_api_data()
915 916 try:
916 917 UserModel().add_extra_email(c.user.user_id, email)
917 918 audit_logger.store_web(
918 919 'user.edit.email.add',
919 920 action_data={'email': email, 'user': user_data},
920 921 user=self._rhodecode_user)
921 922 Session().commit()
922 923 h.flash(_("Added new email address `%s` for user account") % email,
923 924 category='success')
924 925 except formencode.Invalid as error:
925 926 h.flash(h.escape(error.error_dict['email']), category='error')
926 927 except IntegrityError:
927 928 log.warning("Email %s already exists", email)
928 929 h.flash(_('Email `{}` is already registered for another user.').format(email),
929 930 category='error')
930 931 except Exception:
931 932 log.exception("Exception during email saving")
932 933 h.flash(_('An error occurred during email saving'),
933 934 category='error')
934 935 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
935 936
936 937 @LoginRequired()
937 938 @HasPermissionAllDecorator('hg.admin')
938 939 @CSRFRequired()
939 940 @view_config(
940 941 route_name='edit_user_emails_delete', request_method='POST')
941 942 def emails_delete(self):
942 943 _ = self.request.translate
943 944 c = self.load_default_context()
944 945
945 946 user_id = self.db_user_id
946 947 c.user = self.db_user
947 948
948 949 email_id = self.request.POST.get('del_email_id')
949 950 user_model = UserModel()
950 951
951 952 email = UserEmailMap.query().get(email_id).email
952 953 user_data = c.user.get_api_data()
953 954 user_model.delete_extra_email(c.user.user_id, email_id)
954 955 audit_logger.store_web(
955 956 'user.edit.email.delete',
956 957 action_data={'email': email, 'user': user_data},
957 958 user=self._rhodecode_user)
958 959 Session().commit()
959 960 h.flash(_("Removed email address from user account"),
960 961 category='success')
961 962 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
962 963
963 964 @LoginRequired()
964 965 @HasPermissionAllDecorator('hg.admin')
965 966 @view_config(
966 967 route_name='edit_user_ips', request_method='GET',
967 968 renderer='rhodecode:templates/admin/users/user_edit.mako')
968 969 def ips(self):
969 970 _ = self.request.translate
970 971 c = self.load_default_context()
971 972 c.user = self.db_user
972 973
973 974 c.active = 'ips'
974 975 c.user_ip_map = UserIpMap.query() \
975 976 .filter(UserIpMap.user == c.user).all()
976 977
977 978 c.inherit_default_ips = c.user.inherit_default_permissions
978 979 c.default_user_ip_map = UserIpMap.query() \
979 980 .filter(UserIpMap.user == User.get_default_user()).all()
980 981
981 982 return self._get_template_context(c)
982 983
983 984 @LoginRequired()
984 985 @HasPermissionAllDecorator('hg.admin')
985 986 @CSRFRequired()
986 987 @view_config(
987 988 route_name='edit_user_ips_add', request_method='POST')
988 989 # NOTE(marcink): this view is allowed for default users, as we can
989 990 # edit their IP white list
990 991 def ips_add(self):
991 992 _ = self.request.translate
992 993 c = self.load_default_context()
993 994
994 995 user_id = self.db_user_id
995 996 c.user = self.db_user
996 997
997 998 user_model = UserModel()
998 999 desc = self.request.POST.get('description')
999 1000 try:
1000 1001 ip_list = user_model.parse_ip_range(
1001 1002 self.request.POST.get('new_ip'))
1002 1003 except Exception as e:
1003 1004 ip_list = []
1004 1005 log.exception("Exception during ip saving")
1005 1006 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1006 1007 category='error')
1007 1008 added = []
1008 1009 user_data = c.user.get_api_data()
1009 1010 for ip in ip_list:
1010 1011 try:
1011 1012 user_model.add_extra_ip(c.user.user_id, ip, desc)
1012 1013 audit_logger.store_web(
1013 1014 'user.edit.ip.add',
1014 1015 action_data={'ip': ip, 'user': user_data},
1015 1016 user=self._rhodecode_user)
1016 1017 Session().commit()
1017 1018 added.append(ip)
1018 1019 except formencode.Invalid as error:
1019 1020 msg = error.error_dict['ip']
1020 1021 h.flash(msg, category='error')
1021 1022 except Exception:
1022 1023 log.exception("Exception during ip saving")
1023 1024 h.flash(_('An error occurred during ip saving'),
1024 1025 category='error')
1025 1026 if added:
1026 1027 h.flash(
1027 1028 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1028 1029 category='success')
1029 1030 if 'default_user' in self.request.POST:
1030 1031 # case for editing global IP list we do it for 'DEFAULT' user
1031 1032 raise HTTPFound(h.route_path('admin_permissions_ips'))
1032 1033 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1033 1034
1034 1035 @LoginRequired()
1035 1036 @HasPermissionAllDecorator('hg.admin')
1036 1037 @CSRFRequired()
1037 1038 @view_config(
1038 1039 route_name='edit_user_ips_delete', request_method='POST')
1039 1040 # NOTE(marcink): this view is allowed for default users, as we can
1040 1041 # edit their IP white list
1041 1042 def ips_delete(self):
1042 1043 _ = self.request.translate
1043 1044 c = self.load_default_context()
1044 1045
1045 1046 user_id = self.db_user_id
1046 1047 c.user = self.db_user
1047 1048
1048 1049 ip_id = self.request.POST.get('del_ip_id')
1049 1050 user_model = UserModel()
1050 1051 user_data = c.user.get_api_data()
1051 1052 ip = UserIpMap.query().get(ip_id).ip_addr
1052 1053 user_model.delete_extra_ip(c.user.user_id, ip_id)
1053 1054 audit_logger.store_web(
1054 1055 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1055 1056 user=self._rhodecode_user)
1056 1057 Session().commit()
1057 1058 h.flash(_("Removed ip address from user whitelist"), category='success')
1058 1059
1059 1060 if 'default_user' in self.request.POST:
1060 1061 # case for editing global IP list we do it for 'DEFAULT' user
1061 1062 raise HTTPFound(h.route_path('admin_permissions_ips'))
1062 1063 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1063 1064
1064 1065 @LoginRequired()
1065 1066 @HasPermissionAllDecorator('hg.admin')
1066 1067 @view_config(
1067 1068 route_name='edit_user_groups_management', request_method='GET',
1068 1069 renderer='rhodecode:templates/admin/users/user_edit.mako')
1069 1070 def groups_management(self):
1070 1071 c = self.load_default_context()
1071 1072 c.user = self.db_user
1072 1073 c.data = c.user.group_member
1073 1074
1074 1075 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1075 1076 for group in c.user.group_member]
1076 1077 c.groups = json.dumps(groups)
1077 1078 c.active = 'groups'
1078 1079
1079 1080 return self._get_template_context(c)
1080 1081
1081 1082 @LoginRequired()
1082 1083 @HasPermissionAllDecorator('hg.admin')
1083 1084 @CSRFRequired()
1084 1085 @view_config(
1085 1086 route_name='edit_user_groups_management_updates', request_method='POST')
1086 1087 def groups_management_updates(self):
1087 1088 _ = self.request.translate
1088 1089 c = self.load_default_context()
1089 1090
1090 1091 user_id = self.db_user_id
1091 1092 c.user = self.db_user
1092 1093
1093 1094 user_groups = set(self.request.POST.getall('users_group_id'))
1094 1095 user_groups_objects = []
1095 1096
1096 1097 for ugid in user_groups:
1097 1098 user_groups_objects.append(
1098 1099 UserGroupModel().get_group(safe_int(ugid)))
1099 1100 user_group_model = UserGroupModel()
1100 1101 added_to_groups, removed_from_groups = \
1101 1102 user_group_model.change_groups(c.user, user_groups_objects)
1102 1103
1103 1104 user_data = c.user.get_api_data()
1104 1105 for user_group_id in added_to_groups:
1105 1106 user_group = UserGroup.get(user_group_id)
1106 1107 old_values = user_group.get_api_data()
1107 1108 audit_logger.store_web(
1108 1109 'user_group.edit.member.add',
1109 1110 action_data={'user': user_data, 'old_data': old_values},
1110 1111 user=self._rhodecode_user)
1111 1112
1112 1113 for user_group_id in removed_from_groups:
1113 1114 user_group = UserGroup.get(user_group_id)
1114 1115 old_values = user_group.get_api_data()
1115 1116 audit_logger.store_web(
1116 1117 'user_group.edit.member.delete',
1117 1118 action_data={'user': user_data, 'old_data': old_values},
1118 1119 user=self._rhodecode_user)
1119 1120
1120 1121 Session().commit()
1121 1122 c.active = 'user_groups_management'
1122 1123 h.flash(_("Groups successfully changed"), category='success')
1123 1124
1124 1125 return HTTPFound(h.route_path(
1125 1126 'edit_user_groups_management', user_id=user_id))
1126 1127
1127 1128 @LoginRequired()
1128 1129 @HasPermissionAllDecorator('hg.admin')
1129 1130 @view_config(
1130 1131 route_name='edit_user_audit_logs', request_method='GET',
1131 1132 renderer='rhodecode:templates/admin/users/user_edit.mako')
1132 1133 def user_audit_logs(self):
1133 1134 _ = self.request.translate
1134 1135 c = self.load_default_context()
1135 1136 c.user = self.db_user
1136 1137
1137 1138 c.active = 'audit'
1138 1139
1139 1140 p = safe_int(self.request.GET.get('page', 1), 1)
1140 1141
1141 1142 filter_term = self.request.GET.get('filter')
1142 1143 user_log = UserModel().get_user_log(c.user, filter_term)
1143 1144
1144 1145 def url_generator(**kw):
1145 1146 if filter_term:
1146 1147 kw['filter'] = filter_term
1147 1148 return self.request.current_route_path(_query=kw)
1148 1149
1149 1150 c.audit_logs = h.Page(
1150 1151 user_log, page=p, items_per_page=10, url=url_generator)
1151 1152 c.filter_term = filter_term
1152 1153 return self._get_template_context(c)
1153 1154
1154 1155 @LoginRequired()
1155 1156 @HasPermissionAllDecorator('hg.admin')
1156 1157 @view_config(
1157 1158 route_name='edit_user_perms_summary', request_method='GET',
1158 1159 renderer='rhodecode:templates/admin/users/user_edit.mako')
1159 1160 def user_perms_summary(self):
1160 1161 _ = self.request.translate
1161 1162 c = self.load_default_context()
1162 1163 c.user = self.db_user
1163 1164
1164 1165 c.active = 'perms_summary'
1165 1166 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1166 1167
1167 1168 return self._get_template_context(c)
1168 1169
1169 1170 @LoginRequired()
1170 1171 @HasPermissionAllDecorator('hg.admin')
1171 1172 @view_config(
1172 1173 route_name='edit_user_perms_summary_json', request_method='GET',
1173 1174 renderer='json_ext')
1174 1175 def user_perms_summary_json(self):
1175 1176 self.load_default_context()
1176 1177 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1177 1178
1178 1179 return perm_user.permissions
@@ -1,322 +1,325 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 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 re
22 22 import logging
23 23
24 24 from pyramid.view import view_config
25 25
26 26 from rhodecode.apps._base import BaseAppView
27 27 from rhodecode.lib import helpers as h
28 28 from rhodecode.lib.auth import (
29 29 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator)
30 30 from rhodecode.lib.index import searcher_from_config
31 31 from rhodecode.lib.utils2 import safe_unicode, str2bool
32 32 from rhodecode.lib.ext_json import json
33 33 from rhodecode.model.db import (
34 34 func, or_, in_filter_generator, Repository, RepoGroup)
35 35 from rhodecode.model.repo import RepoModel
36 36 from rhodecode.model.repo_group import RepoGroupModel
37 37 from rhodecode.model.scm import RepoGroupList, RepoList
38 38 from rhodecode.model.user import UserModel
39 39 from rhodecode.model.user_group import UserGroupModel
40 40
41 41 log = logging.getLogger(__name__)
42 42
43 43
44 44 class HomeView(BaseAppView):
45 45
46 46 def load_default_context(self):
47 47 c = self._get_local_tmpl_context()
48 48 c.user = c.auth_user.get_instance()
49 49 self._register_global_c(c)
50 50 return c
51 51
52 52 @LoginRequired()
53 53 @view_config(
54 54 route_name='user_autocomplete_data', request_method='GET',
55 55 renderer='json_ext', xhr=True)
56 56 def user_autocomplete_data(self):
57 self.load_default_context()
57 58 query = self.request.GET.get('query')
58 59 active = str2bool(self.request.GET.get('active') or True)
59 60 include_groups = str2bool(self.request.GET.get('user_groups'))
60 61 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
61 62 skip_default_user = str2bool(self.request.GET.get('skip_default_user'))
62 63
63 64 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
64 65 query, active, include_groups)
65 66
66 67 _users = UserModel().get_users(
67 68 name_contains=query, only_active=active)
68 69
69 70 def maybe_skip_default_user(usr):
70 71 if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER:
71 72 return False
72 73 return True
73 74 _users = filter(maybe_skip_default_user, _users)
74 75
75 76 if include_groups:
76 77 # extend with user groups
77 78 _user_groups = UserGroupModel().get_user_groups(
78 79 name_contains=query, only_active=active,
79 80 expand_groups=expand_groups)
80 81 _users = _users + _user_groups
81 82
82 83 return {'suggestions': _users}
83 84
84 85 @LoginRequired()
85 86 @NotAnonymous()
86 87 @view_config(
87 88 route_name='user_group_autocomplete_data', request_method='GET',
88 89 renderer='json_ext', xhr=True)
89 90 def user_group_autocomplete_data(self):
91 self.load_default_context()
90 92 query = self.request.GET.get('query')
91 93 active = str2bool(self.request.GET.get('active') or True)
92 94 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
93 95
94 96 log.debug('generating user group list, query:%s, active:%s',
95 97 query, active)
96 98
97 99 _user_groups = UserGroupModel().get_user_groups(
98 100 name_contains=query, only_active=active,
99 101 expand_groups=expand_groups)
100 102 _user_groups = _user_groups
101 103
102 104 return {'suggestions': _user_groups}
103 105
104 106 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
105 107 allowed_ids = self._rhodecode_user.repo_acl_ids(
106 108 ['repository.read', 'repository.write', 'repository.admin'],
107 109 cache=False, name_filter=name_contains) or [-1]
108 110
109 111 query = Repository.query()\
110 112 .order_by(func.length(Repository.repo_name))\
111 113 .order_by(Repository.repo_name)\
112 114 .filter(or_(
113 115 # generate multiple IN to fix limitation problems
114 116 *in_filter_generator(Repository.repo_id, allowed_ids)
115 117 ))
116 118
117 119 if repo_type:
118 120 query = query.filter(Repository.repo_type == repo_type)
119 121
120 122 if name_contains:
121 123 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
122 124 query = query.filter(
123 125 Repository.repo_name.ilike(ilike_expression))
124 126 query = query.limit(limit)
125 127
126 128 acl_repo_iter = query
127 129
128 130 return [
129 131 {
130 132 'id': obj.repo_name,
131 133 'text': obj.repo_name,
132 134 'type': 'repo',
133 135 'obj': {'repo_type': obj.repo_type, 'private': obj.private,
134 136 'repo_id': obj.repo_id},
135 137 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
136 138 }
137 139 for obj in acl_repo_iter]
138 140
139 141 def _get_repo_group_list(self, name_contains=None, limit=20):
140 142 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
141 143 ['group.read', 'group.write', 'group.admin'],
142 144 cache=False, name_filter=name_contains) or [-1]
143 145
144 146 query = RepoGroup.query()\
145 147 .order_by(func.length(RepoGroup.group_name))\
146 148 .order_by(RepoGroup.group_name) \
147 149 .filter(or_(
148 150 # generate multiple IN to fix limitation problems
149 151 *in_filter_generator(RepoGroup.group_id, allowed_ids)
150 152 ))
151 153
152 154 if name_contains:
153 155 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
154 156 query = query.filter(
155 157 RepoGroup.group_name.ilike(ilike_expression))
156 158 query = query.limit(limit)
157 159
158 160 acl_repo_iter = query
159 161
160 162 return [
161 163 {
162 164 'id': obj.group_name,
163 165 'text': obj.group_name,
164 166 'type': 'group',
165 167 'obj': {},
166 168 'url': h.route_path(
167 169 'repo_group_home', repo_group_name=obj.group_name)
168 170 }
169 171 for obj in acl_repo_iter]
170 172
171 173 def _get_hash_commit_list(self, auth_user, query=None):
172 174 if not query or len(query) < 3:
173 175 return []
174 176
175 177 commit_hashes = re.compile('(?:commit:)([0-9a-f]{2,40})').findall(query)
176 178
177 179 if len(commit_hashes) != 1:
178 180 return []
179 181
180 182 commit_hash_prefix = commit_hashes[0]
181 183
182 184 searcher = searcher_from_config(self.request.registry.settings)
183 185 result = searcher.search(
184 186 'commit_id:%s*' % commit_hash_prefix, 'commit', auth_user,
185 187 raise_on_exc=False)
186 188
187 189 return [
188 190 {
189 191 'id': entry['commit_id'],
190 192 'text': entry['commit_id'],
191 193 'type': 'commit',
192 194 'obj': {'repo': entry['repository']},
193 195 'url': h.route_path(
194 196 'repo_commit',
195 197 repo_name=entry['repository'], commit_id=entry['commit_id'])
196 198 }
197 199 for entry in result['results']]
198 200
199 201 @LoginRequired()
200 202 @view_config(
201 203 route_name='repo_list_data', request_method='GET',
202 204 renderer='json_ext', xhr=True)
203 205 def repo_list_data(self):
204 206 _ = self.request.translate
207 self.load_default_context()
205 208
206 209 query = self.request.GET.get('query')
207 210 repo_type = self.request.GET.get('repo_type')
208 211 log.debug('generating repo list, query:%s, repo_type:%s',
209 212 query, repo_type)
210 213
211 214 res = []
212 215 repos = self._get_repo_list(query, repo_type=repo_type)
213 216 if repos:
214 217 res.append({
215 218 'text': _('Repositories'),
216 219 'children': repos
217 220 })
218 221
219 222 data = {
220 223 'more': False,
221 224 'results': res
222 225 }
223 226 return data
224 227
225 228 @LoginRequired()
226 229 @view_config(
227 230 route_name='goto_switcher_data', request_method='GET',
228 231 renderer='json_ext', xhr=True)
229 232 def goto_switcher_data(self):
230 233 c = self.load_default_context()
231 234
232 235 _ = self.request.translate
233 236
234 237 query = self.request.GET.get('query')
235 238 log.debug('generating goto switcher list, query %s', query)
236 239
237 240 res = []
238 241 repo_groups = self._get_repo_group_list(query)
239 242 if repo_groups:
240 243 res.append({
241 244 'text': _('Groups'),
242 245 'children': repo_groups
243 246 })
244 247
245 248 repos = self._get_repo_list(query)
246 249 if repos:
247 250 res.append({
248 251 'text': _('Repositories'),
249 252 'children': repos
250 253 })
251 254
252 255 commits = self._get_hash_commit_list(c.auth_user, query)
253 256 if commits:
254 257 unique_repos = {}
255 258 for commit in commits:
256 259 unique_repos.setdefault(commit['obj']['repo'], []
257 260 ).append(commit)
258 261
259 262 for repo in unique_repos:
260 263 res.append({
261 264 'text': _('Commits in %(repo)s') % {'repo': repo},
262 265 'children': unique_repos[repo]
263 266 })
264 267
265 268 data = {
266 269 'more': False,
267 270 'results': res
268 271 }
269 272 return data
270 273
271 274 def _get_groups_and_repos(self, repo_group_id=None):
272 275 # repo groups groups
273 276 repo_group_list = RepoGroup.get_all_repo_groups(group_id=repo_group_id)
274 277 _perms = ['group.read', 'group.write', 'group.admin']
275 278 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
276 279 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
277 280 repo_group_list=repo_group_list_acl, admin=False)
278 281
279 282 # repositories
280 283 repo_list = Repository.get_all_repos(group_id=repo_group_id)
281 284 _perms = ['repository.read', 'repository.write', 'repository.admin']
282 285 repo_list_acl = RepoList(repo_list, perm_set=_perms)
283 286 repo_data = RepoModel().get_repos_as_dict(
284 287 repo_list=repo_list_acl, admin=False)
285 288
286 289 return repo_data, repo_group_data
287 290
288 291 @LoginRequired()
289 292 @view_config(
290 293 route_name='home', request_method='GET',
291 294 renderer='rhodecode:templates/index.mako')
292 295 def main_page(self):
293 296 c = self.load_default_context()
294 297 c.repo_group = None
295 298
296 299 repo_data, repo_group_data = self._get_groups_and_repos()
297 300 # json used to render the grids
298 301 c.repos_data = json.dumps(repo_data)
299 302 c.repo_groups_data = json.dumps(repo_group_data)
300 303
301 304 return self._get_template_context(c)
302 305
303 306 @LoginRequired()
304 307 @HasRepoGroupPermissionAnyDecorator(
305 308 'group.read', 'group.write', 'group.admin')
306 309 @view_config(
307 310 route_name='repo_group_home', request_method='GET',
308 311 renderer='rhodecode:templates/index_repo_group.mako')
309 312 @view_config(
310 313 route_name='repo_group_home_slash', request_method='GET',
311 314 renderer='rhodecode:templates/index_repo_group.mako')
312 315 def repo_group_main_page(self):
313 316 c = self.load_default_context()
314 317 c.repo_group = self.request.db_repo_group
315 318 repo_data, repo_group_data = self._get_groups_and_repos(
316 319 c.repo_group.group_id)
317 320
318 321 # json used to render the grids
319 322 c.repos_data = json.dumps(repo_data)
320 323 c.repo_groups_data = json.dumps(repo_group_data)
321 324
322 325 return self._get_template_context(c)
General Comments 0
You need to be logged in to leave comments. Login now