##// END OF EJS Templates
users: personal repo group template also can use first/last names for an user
marcink -
r3659:ccf1c60d default
parent child Browse files
Show More
@@ -1,1266 +1,1265 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 31 from rhodecode import events
32 32 from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView
33 33 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
34 34 from rhodecode.authentication.plugins import auth_rhodecode
35 35 from rhodecode.events import trigger
36 36 from rhodecode.model.db import true
37 37
38 38 from rhodecode.lib import audit_logger, rc_cache
39 39 from rhodecode.lib.exceptions import (
40 40 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
41 41 UserOwnsUserGroupsException, DefaultUserException)
42 42 from rhodecode.lib.ext_json import json
43 43 from rhodecode.lib.auth import (
44 44 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
45 45 from rhodecode.lib import helpers as h
46 46 from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict
47 47 from rhodecode.model.auth_token import AuthTokenModel
48 48 from rhodecode.model.forms import (
49 49 UserForm, UserIndividualPermissionsForm, UserPermissionsForm,
50 50 UserExtraEmailForm, UserExtraIpForm)
51 51 from rhodecode.model.permission import PermissionModel
52 52 from rhodecode.model.repo_group import RepoGroupModel
53 53 from rhodecode.model.ssh_key import SshKeyModel
54 54 from rhodecode.model.user import UserModel
55 55 from rhodecode.model.user_group import UserGroupModel
56 56 from rhodecode.model.db import (
57 57 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
58 58 UserApiKeys, UserSshKeys, RepoGroup)
59 59 from rhodecode.model.meta import Session
60 60
61 61 log = logging.getLogger(__name__)
62 62
63 63
64 64 class AdminUsersView(BaseAppView, DataGridAppView):
65 65
66 66 def load_default_context(self):
67 67 c = self._get_local_tmpl_context()
68 68 return c
69 69
70 70 @LoginRequired()
71 71 @HasPermissionAllDecorator('hg.admin')
72 72 @view_config(
73 73 route_name='users', request_method='GET',
74 74 renderer='rhodecode:templates/admin/users/users.mako')
75 75 def users_list(self):
76 76 c = self.load_default_context()
77 77 return self._get_template_context(c)
78 78
79 79 @LoginRequired()
80 80 @HasPermissionAllDecorator('hg.admin')
81 81 @view_config(
82 82 # renderer defined below
83 83 route_name='users_data', request_method='GET',
84 84 renderer='json_ext', xhr=True)
85 85 def users_list_data(self):
86 86 self.load_default_context()
87 87 column_map = {
88 88 'first_name': 'name',
89 89 'last_name': 'lastname',
90 90 }
91 91 draw, start, limit = self._extract_chunk(self.request)
92 92 search_q, order_by, order_dir = self._extract_ordering(
93 93 self.request, column_map=column_map)
94 94 _render = self.request.get_partial_renderer(
95 95 'rhodecode:templates/data_table/_dt_elements.mako')
96 96
97 97 def user_actions(user_id, username):
98 98 return _render("user_actions", user_id, username)
99 99
100 100 users_data_total_count = User.query()\
101 101 .filter(User.username != User.DEFAULT_USER) \
102 102 .count()
103 103
104 104 users_data_total_inactive_count = User.query()\
105 105 .filter(User.username != User.DEFAULT_USER) \
106 106 .filter(User.active != true())\
107 107 .count()
108 108
109 109 # json generate
110 110 base_q = User.query().filter(User.username != User.DEFAULT_USER)
111 111 base_inactive_q = base_q.filter(User.active != true())
112 112
113 113 if search_q:
114 114 like_expression = u'%{}%'.format(safe_unicode(search_q))
115 115 base_q = base_q.filter(or_(
116 116 User.username.ilike(like_expression),
117 117 User._email.ilike(like_expression),
118 118 User.name.ilike(like_expression),
119 119 User.lastname.ilike(like_expression),
120 120 ))
121 121 base_inactive_q = base_q.filter(User.active != true())
122 122
123 123 users_data_total_filtered_count = base_q.count()
124 124 users_data_total_filtered_inactive_count = base_inactive_q.count()
125 125
126 126 sort_col = getattr(User, order_by, None)
127 127 if sort_col:
128 128 if order_dir == 'asc':
129 129 # handle null values properly to order by NULL last
130 130 if order_by in ['last_activity']:
131 131 sort_col = coalesce(sort_col, datetime.date.max)
132 132 sort_col = sort_col.asc()
133 133 else:
134 134 # handle null values properly to order by NULL last
135 135 if order_by in ['last_activity']:
136 136 sort_col = coalesce(sort_col, datetime.date.min)
137 137 sort_col = sort_col.desc()
138 138
139 139 base_q = base_q.order_by(sort_col)
140 140 base_q = base_q.offset(start).limit(limit)
141 141
142 142 users_list = base_q.all()
143 143
144 144 users_data = []
145 145 for user in users_list:
146 146 users_data.append({
147 147 "username": h.gravatar_with_user(self.request, user.username),
148 148 "email": user.email,
149 149 "first_name": user.first_name,
150 150 "last_name": user.last_name,
151 151 "last_login": h.format_date(user.last_login),
152 152 "last_activity": h.format_date(user.last_activity),
153 153 "active": h.bool2icon(user.active),
154 154 "active_raw": user.active,
155 155 "admin": h.bool2icon(user.admin),
156 156 "extern_type": user.extern_type,
157 157 "extern_name": user.extern_name,
158 158 "action": user_actions(user.user_id, user.username),
159 159 })
160 160 data = ({
161 161 'draw': draw,
162 162 'data': users_data,
163 163 'recordsTotal': users_data_total_count,
164 164 'recordsFiltered': users_data_total_filtered_count,
165 165 'recordsTotalInactive': users_data_total_inactive_count,
166 166 'recordsFilteredInactive': users_data_total_filtered_inactive_count
167 167 })
168 168
169 169 return data
170 170
171 171 def _set_personal_repo_group_template_vars(self, c_obj):
172 172 DummyUser = AttributeDict({
173 173 'username': '${username}',
174 174 'user_id': '${user_id}',
175 175 })
176 176 c_obj.default_create_repo_group = RepoGroupModel() \
177 177 .get_default_create_personal_repo_group()
178 178 c_obj.personal_repo_group_name = RepoGroupModel() \
179 179 .get_personal_group_name(DummyUser)
180 180
181 181 @LoginRequired()
182 182 @HasPermissionAllDecorator('hg.admin')
183 183 @view_config(
184 184 route_name='users_new', request_method='GET',
185 185 renderer='rhodecode:templates/admin/users/user_add.mako')
186 186 def users_new(self):
187 187 _ = self.request.translate
188 188 c = self.load_default_context()
189 189 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
190 190 self._set_personal_repo_group_template_vars(c)
191 191 return self._get_template_context(c)
192 192
193 193 @LoginRequired()
194 194 @HasPermissionAllDecorator('hg.admin')
195 195 @CSRFRequired()
196 196 @view_config(
197 197 route_name='users_create', request_method='POST',
198 198 renderer='rhodecode:templates/admin/users/user_add.mako')
199 199 def users_create(self):
200 200 _ = self.request.translate
201 201 c = self.load_default_context()
202 202 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
203 203 user_model = UserModel()
204 204 user_form = UserForm(self.request.translate)()
205 205 try:
206 206 form_result = user_form.to_python(dict(self.request.POST))
207 207 user = user_model.create(form_result)
208 208 Session().flush()
209 209 creation_data = user.get_api_data()
210 210 username = form_result['username']
211 211
212 212 audit_logger.store_web(
213 213 'user.create', action_data={'data': creation_data},
214 214 user=c.rhodecode_user)
215 215
216 216 user_link = h.link_to(
217 217 h.escape(username),
218 218 h.route_path('user_edit', user_id=user.user_id))
219 219 h.flash(h.literal(_('Created user %(user_link)s')
220 220 % {'user_link': user_link}), category='success')
221 221 Session().commit()
222 222 except formencode.Invalid as errors:
223 223 self._set_personal_repo_group_template_vars(c)
224 224 data = render(
225 225 'rhodecode:templates/admin/users/user_add.mako',
226 226 self._get_template_context(c), self.request)
227 227 html = formencode.htmlfill.render(
228 228 data,
229 229 defaults=errors.value,
230 230 errors=errors.error_dict or {},
231 231 prefix_error=False,
232 232 encoding="UTF-8",
233 233 force_defaults=False
234 234 )
235 235 return Response(html)
236 236 except UserCreationError as e:
237 237 h.flash(e, 'error')
238 238 except Exception:
239 239 log.exception("Exception creation of user")
240 240 h.flash(_('Error occurred during creation of user %s')
241 241 % self.request.POST.get('username'), category='error')
242 242 raise HTTPFound(h.route_path('users'))
243 243
244 244
245 245 class UsersView(UserAppView):
246 246 ALLOW_SCOPED_TOKENS = False
247 247 """
248 248 This view has alternative version inside EE, if modified please take a look
249 249 in there as well.
250 250 """
251 251
252 252 def load_default_context(self):
253 253 c = self._get_local_tmpl_context()
254 254 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
255 255 c.allowed_languages = [
256 256 ('en', 'English (en)'),
257 257 ('de', 'German (de)'),
258 258 ('fr', 'French (fr)'),
259 259 ('it', 'Italian (it)'),
260 260 ('ja', 'Japanese (ja)'),
261 261 ('pl', 'Polish (pl)'),
262 262 ('pt', 'Portuguese (pt)'),
263 263 ('ru', 'Russian (ru)'),
264 264 ('zh', 'Chinese (zh)'),
265 265 ]
266 266 req = self.request
267 267
268 268 c.available_permissions = req.registry.settings['available_permissions']
269 269 PermissionModel().set_global_permission_choices(
270 270 c, gettext_translator=req.translate)
271 271
272 272 return c
273 273
274 274 @LoginRequired()
275 275 @HasPermissionAllDecorator('hg.admin')
276 276 @CSRFRequired()
277 277 @view_config(
278 278 route_name='user_update', request_method='POST',
279 279 renderer='rhodecode:templates/admin/users/user_edit.mako')
280 280 def user_update(self):
281 281 _ = self.request.translate
282 282 c = self.load_default_context()
283 283
284 284 user_id = self.db_user_id
285 285 c.user = self.db_user
286 286
287 287 c.active = 'profile'
288 288 c.extern_type = c.user.extern_type
289 289 c.extern_name = c.user.extern_name
290 290 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
291 291 available_languages = [x[0] for x in c.allowed_languages]
292 292 _form = UserForm(self.request.translate, edit=True,
293 293 available_languages=available_languages,
294 294 old_data={'user_id': user_id,
295 295 'email': c.user.email})()
296 296 form_result = {}
297 297 old_values = c.user.get_api_data()
298 298 try:
299 299 form_result = _form.to_python(dict(self.request.POST))
300 300 skip_attrs = ['extern_type', 'extern_name']
301 301 # TODO: plugin should define if username can be updated
302 302 if c.extern_type != "rhodecode":
303 303 # forbid updating username for external accounts
304 304 skip_attrs.append('username')
305 305
306 306 UserModel().update_user(
307 307 user_id, skip_attrs=skip_attrs, **form_result)
308 308
309 309 audit_logger.store_web(
310 310 'user.edit', action_data={'old_data': old_values},
311 311 user=c.rhodecode_user)
312 312
313 313 Session().commit()
314 314 h.flash(_('User updated successfully'), category='success')
315 315 except formencode.Invalid as errors:
316 316 data = render(
317 317 'rhodecode:templates/admin/users/user_edit.mako',
318 318 self._get_template_context(c), self.request)
319 319 html = formencode.htmlfill.render(
320 320 data,
321 321 defaults=errors.value,
322 322 errors=errors.error_dict or {},
323 323 prefix_error=False,
324 324 encoding="UTF-8",
325 325 force_defaults=False
326 326 )
327 327 return Response(html)
328 328 except UserCreationError as e:
329 329 h.flash(e, 'error')
330 330 except Exception:
331 331 log.exception("Exception updating user")
332 332 h.flash(_('Error occurred during update of user %s')
333 333 % form_result.get('username'), category='error')
334 334 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
335 335
336 336 @LoginRequired()
337 337 @HasPermissionAllDecorator('hg.admin')
338 338 @CSRFRequired()
339 339 @view_config(
340 340 route_name='user_delete', request_method='POST',
341 341 renderer='rhodecode:templates/admin/users/user_edit.mako')
342 342 def user_delete(self):
343 343 _ = self.request.translate
344 344 c = self.load_default_context()
345 345 c.user = self.db_user
346 346
347 347 _repos = c.user.repositories
348 348 _repo_groups = c.user.repository_groups
349 349 _user_groups = c.user.user_groups
350 350
351 351 handle_repos = None
352 352 handle_repo_groups = None
353 353 handle_user_groups = None
354 354 # dummy call for flash of handle
355 355 set_handle_flash_repos = lambda: None
356 356 set_handle_flash_repo_groups = lambda: None
357 357 set_handle_flash_user_groups = lambda: None
358 358
359 359 if _repos and self.request.POST.get('user_repos'):
360 360 do = self.request.POST['user_repos']
361 361 if do == 'detach':
362 362 handle_repos = 'detach'
363 363 set_handle_flash_repos = lambda: h.flash(
364 364 _('Detached %s repositories') % len(_repos),
365 365 category='success')
366 366 elif do == 'delete':
367 367 handle_repos = 'delete'
368 368 set_handle_flash_repos = lambda: h.flash(
369 369 _('Deleted %s repositories') % len(_repos),
370 370 category='success')
371 371
372 372 if _repo_groups and self.request.POST.get('user_repo_groups'):
373 373 do = self.request.POST['user_repo_groups']
374 374 if do == 'detach':
375 375 handle_repo_groups = 'detach'
376 376 set_handle_flash_repo_groups = lambda: h.flash(
377 377 _('Detached %s repository groups') % len(_repo_groups),
378 378 category='success')
379 379 elif do == 'delete':
380 380 handle_repo_groups = 'delete'
381 381 set_handle_flash_repo_groups = lambda: h.flash(
382 382 _('Deleted %s repository groups') % len(_repo_groups),
383 383 category='success')
384 384
385 385 if _user_groups and self.request.POST.get('user_user_groups'):
386 386 do = self.request.POST['user_user_groups']
387 387 if do == 'detach':
388 388 handle_user_groups = 'detach'
389 389 set_handle_flash_user_groups = lambda: h.flash(
390 390 _('Detached %s user groups') % len(_user_groups),
391 391 category='success')
392 392 elif do == 'delete':
393 393 handle_user_groups = 'delete'
394 394 set_handle_flash_user_groups = lambda: h.flash(
395 395 _('Deleted %s user groups') % len(_user_groups),
396 396 category='success')
397 397
398 398 old_values = c.user.get_api_data()
399 399 try:
400 400 UserModel().delete(c.user, handle_repos=handle_repos,
401 401 handle_repo_groups=handle_repo_groups,
402 402 handle_user_groups=handle_user_groups)
403 403
404 404 audit_logger.store_web(
405 405 'user.delete', action_data={'old_data': old_values},
406 406 user=c.rhodecode_user)
407 407
408 408 Session().commit()
409 409 set_handle_flash_repos()
410 410 set_handle_flash_repo_groups()
411 411 set_handle_flash_user_groups()
412 412 h.flash(_('Successfully deleted user'), category='success')
413 413 except (UserOwnsReposException, UserOwnsRepoGroupsException,
414 414 UserOwnsUserGroupsException, DefaultUserException) as e:
415 415 h.flash(e, category='warning')
416 416 except Exception:
417 417 log.exception("Exception during deletion of user")
418 418 h.flash(_('An error occurred during deletion of user'),
419 419 category='error')
420 420 raise HTTPFound(h.route_path('users'))
421 421
422 422 @LoginRequired()
423 423 @HasPermissionAllDecorator('hg.admin')
424 424 @view_config(
425 425 route_name='user_edit', request_method='GET',
426 426 renderer='rhodecode:templates/admin/users/user_edit.mako')
427 427 def user_edit(self):
428 428 _ = self.request.translate
429 429 c = self.load_default_context()
430 430 c.user = self.db_user
431 431
432 432 c.active = 'profile'
433 433 c.extern_type = c.user.extern_type
434 434 c.extern_name = c.user.extern_name
435 435 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
436 436
437 437 defaults = c.user.get_dict()
438 438 defaults.update({'language': c.user.user_data.get('language')})
439 439
440 440 data = render(
441 441 'rhodecode:templates/admin/users/user_edit.mako',
442 442 self._get_template_context(c), self.request)
443 443 html = formencode.htmlfill.render(
444 444 data,
445 445 defaults=defaults,
446 446 encoding="UTF-8",
447 447 force_defaults=False
448 448 )
449 449 return Response(html)
450 450
451 451 @LoginRequired()
452 452 @HasPermissionAllDecorator('hg.admin')
453 453 @view_config(
454 454 route_name='user_edit_advanced', request_method='GET',
455 455 renderer='rhodecode:templates/admin/users/user_edit.mako')
456 456 def user_edit_advanced(self):
457 457 _ = self.request.translate
458 458 c = self.load_default_context()
459 459
460 460 user_id = self.db_user_id
461 461 c.user = self.db_user
462 462
463 463 c.active = 'advanced'
464 464 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
465 465 c.personal_repo_group_name = RepoGroupModel()\
466 466 .get_personal_group_name(c.user)
467 467
468 468 c.user_to_review_rules = sorted(
469 469 (x.user for x in c.user.user_review_rules),
470 470 key=lambda u: u.username.lower())
471 471
472 472 c.first_admin = User.get_first_super_admin()
473 473 defaults = c.user.get_dict()
474 474
475 475 # Interim workaround if the user participated on any pull requests as a
476 476 # reviewer.
477 477 has_review = len(c.user.reviewer_pull_requests)
478 478 c.can_delete_user = not has_review
479 479 c.can_delete_user_message = ''
480 480 inactive_link = h.link_to(
481 481 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
482 482 if has_review == 1:
483 483 c.can_delete_user_message = h.literal(_(
484 484 'The user participates as reviewer in {} pull request and '
485 485 'cannot be deleted. \nYou can set the user to '
486 486 '"{}" instead of deleting it.').format(
487 487 has_review, inactive_link))
488 488 elif has_review:
489 489 c.can_delete_user_message = h.literal(_(
490 490 'The user participates as reviewer in {} pull requests and '
491 491 'cannot be deleted. \nYou can set the user to '
492 492 '"{}" instead of deleting it.').format(
493 493 has_review, inactive_link))
494 494
495 495 data = render(
496 496 'rhodecode:templates/admin/users/user_edit.mako',
497 497 self._get_template_context(c), self.request)
498 498 html = formencode.htmlfill.render(
499 499 data,
500 500 defaults=defaults,
501 501 encoding="UTF-8",
502 502 force_defaults=False
503 503 )
504 504 return Response(html)
505 505
506 506 @LoginRequired()
507 507 @HasPermissionAllDecorator('hg.admin')
508 508 @view_config(
509 509 route_name='user_edit_global_perms', request_method='GET',
510 510 renderer='rhodecode:templates/admin/users/user_edit.mako')
511 511 def user_edit_global_perms(self):
512 512 _ = self.request.translate
513 513 c = self.load_default_context()
514 514 c.user = self.db_user
515 515
516 516 c.active = 'global_perms'
517 517
518 518 c.default_user = User.get_default_user()
519 519 defaults = c.user.get_dict()
520 520 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
521 521 defaults.update(c.default_user.get_default_perms())
522 522 defaults.update(c.user.get_default_perms())
523 523
524 524 data = render(
525 525 'rhodecode:templates/admin/users/user_edit.mako',
526 526 self._get_template_context(c), self.request)
527 527 html = formencode.htmlfill.render(
528 528 data,
529 529 defaults=defaults,
530 530 encoding="UTF-8",
531 531 force_defaults=False
532 532 )
533 533 return Response(html)
534 534
535 535 @LoginRequired()
536 536 @HasPermissionAllDecorator('hg.admin')
537 537 @CSRFRequired()
538 538 @view_config(
539 539 route_name='user_edit_global_perms_update', request_method='POST',
540 540 renderer='rhodecode:templates/admin/users/user_edit.mako')
541 541 def user_edit_global_perms_update(self):
542 542 _ = self.request.translate
543 543 c = self.load_default_context()
544 544
545 545 user_id = self.db_user_id
546 546 c.user = self.db_user
547 547
548 548 c.active = 'global_perms'
549 549 try:
550 550 # first stage that verifies the checkbox
551 551 _form = UserIndividualPermissionsForm(self.request.translate)
552 552 form_result = _form.to_python(dict(self.request.POST))
553 553 inherit_perms = form_result['inherit_default_permissions']
554 554 c.user.inherit_default_permissions = inherit_perms
555 555 Session().add(c.user)
556 556
557 557 if not inherit_perms:
558 558 # only update the individual ones if we un check the flag
559 559 _form = UserPermissionsForm(
560 560 self.request.translate,
561 561 [x[0] for x in c.repo_create_choices],
562 562 [x[0] for x in c.repo_create_on_write_choices],
563 563 [x[0] for x in c.repo_group_create_choices],
564 564 [x[0] for x in c.user_group_create_choices],
565 565 [x[0] for x in c.fork_choices],
566 566 [x[0] for x in c.inherit_default_permission_choices])()
567 567
568 568 form_result = _form.to_python(dict(self.request.POST))
569 569 form_result.update({'perm_user_id': c.user.user_id})
570 570
571 571 PermissionModel().update_user_permissions(form_result)
572 572
573 573 # TODO(marcink): implement global permissions
574 574 # audit_log.store_web('user.edit.permissions')
575 575
576 576 Session().commit()
577 577
578 578 h.flash(_('User global permissions updated successfully'),
579 579 category='success')
580 580
581 581 except formencode.Invalid as errors:
582 582 data = render(
583 583 'rhodecode:templates/admin/users/user_edit.mako',
584 584 self._get_template_context(c), self.request)
585 585 html = formencode.htmlfill.render(
586 586 data,
587 587 defaults=errors.value,
588 588 errors=errors.error_dict or {},
589 589 prefix_error=False,
590 590 encoding="UTF-8",
591 591 force_defaults=False
592 592 )
593 593 return Response(html)
594 594 except Exception:
595 595 log.exception("Exception during permissions saving")
596 596 h.flash(_('An error occurred during permissions saving'),
597 597 category='error')
598 598
599 599 affected_user_ids = [user_id]
600 600 events.trigger(events.UserPermissionsChange(affected_user_ids))
601 601 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
602 602
603 603 @LoginRequired()
604 604 @HasPermissionAllDecorator('hg.admin')
605 605 @CSRFRequired()
606 606 @view_config(
607 607 route_name='user_enable_force_password_reset', request_method='POST',
608 608 renderer='rhodecode:templates/admin/users/user_edit.mako')
609 609 def user_enable_force_password_reset(self):
610 610 _ = self.request.translate
611 611 c = self.load_default_context()
612 612
613 613 user_id = self.db_user_id
614 614 c.user = self.db_user
615 615
616 616 try:
617 617 c.user.update_userdata(force_password_change=True)
618 618
619 619 msg = _('Force password change enabled for user')
620 620 audit_logger.store_web('user.edit.password_reset.enabled',
621 621 user=c.rhodecode_user)
622 622
623 623 Session().commit()
624 624 h.flash(msg, category='success')
625 625 except Exception:
626 626 log.exception("Exception during password reset for user")
627 627 h.flash(_('An error occurred during password reset for user'),
628 628 category='error')
629 629
630 630 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
631 631
632 632 @LoginRequired()
633 633 @HasPermissionAllDecorator('hg.admin')
634 634 @CSRFRequired()
635 635 @view_config(
636 636 route_name='user_disable_force_password_reset', request_method='POST',
637 637 renderer='rhodecode:templates/admin/users/user_edit.mako')
638 638 def user_disable_force_password_reset(self):
639 639 _ = self.request.translate
640 640 c = self.load_default_context()
641 641
642 642 user_id = self.db_user_id
643 643 c.user = self.db_user
644 644
645 645 try:
646 646 c.user.update_userdata(force_password_change=False)
647 647
648 648 msg = _('Force password change disabled for user')
649 649 audit_logger.store_web(
650 650 'user.edit.password_reset.disabled',
651 651 user=c.rhodecode_user)
652 652
653 653 Session().commit()
654 654 h.flash(msg, category='success')
655 655 except Exception:
656 656 log.exception("Exception during password reset for user")
657 657 h.flash(_('An error occurred during password reset for user'),
658 658 category='error')
659 659
660 660 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
661 661
662 662 @LoginRequired()
663 663 @HasPermissionAllDecorator('hg.admin')
664 664 @CSRFRequired()
665 665 @view_config(
666 666 route_name='user_create_personal_repo_group', request_method='POST',
667 667 renderer='rhodecode:templates/admin/users/user_edit.mako')
668 668 def user_create_personal_repo_group(self):
669 669 """
670 670 Create personal repository group for this user
671 671 """
672 672 from rhodecode.model.repo_group import RepoGroupModel
673 673
674 674 _ = self.request.translate
675 675 c = self.load_default_context()
676 676
677 677 user_id = self.db_user_id
678 678 c.user = self.db_user
679 679
680 680 personal_repo_group = RepoGroup.get_user_personal_repo_group(
681 681 c.user.user_id)
682 682 if personal_repo_group:
683 683 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
684 684
685 personal_repo_group_name = RepoGroupModel().get_personal_group_name(
686 c.user)
685 personal_repo_group_name = RepoGroupModel().get_personal_group_name(c.user)
687 686 named_personal_group = RepoGroup.get_by_group_name(
688 687 personal_repo_group_name)
689 688 try:
690 689
691 690 if named_personal_group and named_personal_group.user_id == c.user.user_id:
692 691 # migrate the same named group, and mark it as personal
693 692 named_personal_group.personal = True
694 693 Session().add(named_personal_group)
695 694 Session().commit()
696 695 msg = _('Linked repository group `%s` as personal' % (
697 696 personal_repo_group_name,))
698 697 h.flash(msg, category='success')
699 698 elif not named_personal_group:
700 699 RepoGroupModel().create_personal_repo_group(c.user)
701 700
702 701 msg = _('Created repository group `%s`' % (
703 702 personal_repo_group_name,))
704 703 h.flash(msg, category='success')
705 704 else:
706 705 msg = _('Repository group `%s` is already taken' % (
707 706 personal_repo_group_name,))
708 707 h.flash(msg, category='warning')
709 708 except Exception:
710 709 log.exception("Exception during repository group creation")
711 710 msg = _(
712 711 'An error occurred during repository group creation for user')
713 712 h.flash(msg, category='error')
714 713 Session().rollback()
715 714
716 715 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
717 716
718 717 @LoginRequired()
719 718 @HasPermissionAllDecorator('hg.admin')
720 719 @view_config(
721 720 route_name='edit_user_auth_tokens', request_method='GET',
722 721 renderer='rhodecode:templates/admin/users/user_edit.mako')
723 722 def auth_tokens(self):
724 723 _ = self.request.translate
725 724 c = self.load_default_context()
726 725 c.user = self.db_user
727 726
728 727 c.active = 'auth_tokens'
729 728
730 729 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
731 730 c.role_values = [
732 731 (x, AuthTokenModel.cls._get_role_name(x))
733 732 for x in AuthTokenModel.cls.ROLES]
734 733 c.role_options = [(c.role_values, _("Role"))]
735 734 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
736 735 c.user.user_id, show_expired=True)
737 736 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
738 737 return self._get_template_context(c)
739 738
740 739 def maybe_attach_token_scope(self, token):
741 740 # implemented in EE edition
742 741 pass
743 742
744 743 @LoginRequired()
745 744 @HasPermissionAllDecorator('hg.admin')
746 745 @CSRFRequired()
747 746 @view_config(
748 747 route_name='edit_user_auth_tokens_add', request_method='POST')
749 748 def auth_tokens_add(self):
750 749 _ = self.request.translate
751 750 c = self.load_default_context()
752 751
753 752 user_id = self.db_user_id
754 753 c.user = self.db_user
755 754
756 755 user_data = c.user.get_api_data()
757 756 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
758 757 description = self.request.POST.get('description')
759 758 role = self.request.POST.get('role')
760 759
761 760 token = UserModel().add_auth_token(
762 761 user=c.user.user_id,
763 762 lifetime_minutes=lifetime, role=role, description=description,
764 763 scope_callback=self.maybe_attach_token_scope)
765 764 token_data = token.get_api_data()
766 765
767 766 audit_logger.store_web(
768 767 'user.edit.token.add', action_data={
769 768 'data': {'token': token_data, 'user': user_data}},
770 769 user=self._rhodecode_user, )
771 770 Session().commit()
772 771
773 772 h.flash(_("Auth token successfully created"), category='success')
774 773 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
775 774
776 775 @LoginRequired()
777 776 @HasPermissionAllDecorator('hg.admin')
778 777 @CSRFRequired()
779 778 @view_config(
780 779 route_name='edit_user_auth_tokens_delete', request_method='POST')
781 780 def auth_tokens_delete(self):
782 781 _ = self.request.translate
783 782 c = self.load_default_context()
784 783
785 784 user_id = self.db_user_id
786 785 c.user = self.db_user
787 786
788 787 user_data = c.user.get_api_data()
789 788
790 789 del_auth_token = self.request.POST.get('del_auth_token')
791 790
792 791 if del_auth_token:
793 792 token = UserApiKeys.get_or_404(del_auth_token)
794 793 token_data = token.get_api_data()
795 794
796 795 AuthTokenModel().delete(del_auth_token, c.user.user_id)
797 796 audit_logger.store_web(
798 797 'user.edit.token.delete', action_data={
799 798 'data': {'token': token_data, 'user': user_data}},
800 799 user=self._rhodecode_user,)
801 800 Session().commit()
802 801 h.flash(_("Auth token successfully deleted"), category='success')
803 802
804 803 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
805 804
806 805 @LoginRequired()
807 806 @HasPermissionAllDecorator('hg.admin')
808 807 @view_config(
809 808 route_name='edit_user_ssh_keys', request_method='GET',
810 809 renderer='rhodecode:templates/admin/users/user_edit.mako')
811 810 def ssh_keys(self):
812 811 _ = self.request.translate
813 812 c = self.load_default_context()
814 813 c.user = self.db_user
815 814
816 815 c.active = 'ssh_keys'
817 816 c.default_key = self.request.GET.get('default_key')
818 817 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
819 818 return self._get_template_context(c)
820 819
821 820 @LoginRequired()
822 821 @HasPermissionAllDecorator('hg.admin')
823 822 @view_config(
824 823 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
825 824 renderer='rhodecode:templates/admin/users/user_edit.mako')
826 825 def ssh_keys_generate_keypair(self):
827 826 _ = self.request.translate
828 827 c = self.load_default_context()
829 828
830 829 c.user = self.db_user
831 830
832 831 c.active = 'ssh_keys_generate'
833 832 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
834 833 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
835 834
836 835 return self._get_template_context(c)
837 836
838 837 @LoginRequired()
839 838 @HasPermissionAllDecorator('hg.admin')
840 839 @CSRFRequired()
841 840 @view_config(
842 841 route_name='edit_user_ssh_keys_add', request_method='POST')
843 842 def ssh_keys_add(self):
844 843 _ = self.request.translate
845 844 c = self.load_default_context()
846 845
847 846 user_id = self.db_user_id
848 847 c.user = self.db_user
849 848
850 849 user_data = c.user.get_api_data()
851 850 key_data = self.request.POST.get('key_data')
852 851 description = self.request.POST.get('description')
853 852
854 853 fingerprint = 'unknown'
855 854 try:
856 855 if not key_data:
857 856 raise ValueError('Please add a valid public key')
858 857
859 858 key = SshKeyModel().parse_key(key_data.strip())
860 859 fingerprint = key.hash_md5()
861 860
862 861 ssh_key = SshKeyModel().create(
863 862 c.user.user_id, fingerprint, key.keydata, description)
864 863 ssh_key_data = ssh_key.get_api_data()
865 864
866 865 audit_logger.store_web(
867 866 'user.edit.ssh_key.add', action_data={
868 867 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
869 868 user=self._rhodecode_user, )
870 869 Session().commit()
871 870
872 871 # Trigger an event on change of keys.
873 872 trigger(SshKeyFileChangeEvent(), self.request.registry)
874 873
875 874 h.flash(_("Ssh Key successfully created"), category='success')
876 875
877 876 except IntegrityError:
878 877 log.exception("Exception during ssh key saving")
879 878 err = 'Such key with fingerprint `{}` already exists, ' \
880 879 'please use a different one'.format(fingerprint)
881 880 h.flash(_('An error occurred during ssh key saving: {}').format(err),
882 881 category='error')
883 882 except Exception as e:
884 883 log.exception("Exception during ssh key saving")
885 884 h.flash(_('An error occurred during ssh key saving: {}').format(e),
886 885 category='error')
887 886
888 887 return HTTPFound(
889 888 h.route_path('edit_user_ssh_keys', user_id=user_id))
890 889
891 890 @LoginRequired()
892 891 @HasPermissionAllDecorator('hg.admin')
893 892 @CSRFRequired()
894 893 @view_config(
895 894 route_name='edit_user_ssh_keys_delete', request_method='POST')
896 895 def ssh_keys_delete(self):
897 896 _ = self.request.translate
898 897 c = self.load_default_context()
899 898
900 899 user_id = self.db_user_id
901 900 c.user = self.db_user
902 901
903 902 user_data = c.user.get_api_data()
904 903
905 904 del_ssh_key = self.request.POST.get('del_ssh_key')
906 905
907 906 if del_ssh_key:
908 907 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
909 908 ssh_key_data = ssh_key.get_api_data()
910 909
911 910 SshKeyModel().delete(del_ssh_key, c.user.user_id)
912 911 audit_logger.store_web(
913 912 'user.edit.ssh_key.delete', action_data={
914 913 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
915 914 user=self._rhodecode_user,)
916 915 Session().commit()
917 916 # Trigger an event on change of keys.
918 917 trigger(SshKeyFileChangeEvent(), self.request.registry)
919 918 h.flash(_("Ssh key successfully deleted"), category='success')
920 919
921 920 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
922 921
923 922 @LoginRequired()
924 923 @HasPermissionAllDecorator('hg.admin')
925 924 @view_config(
926 925 route_name='edit_user_emails', request_method='GET',
927 926 renderer='rhodecode:templates/admin/users/user_edit.mako')
928 927 def emails(self):
929 928 _ = self.request.translate
930 929 c = self.load_default_context()
931 930 c.user = self.db_user
932 931
933 932 c.active = 'emails'
934 933 c.user_email_map = UserEmailMap.query() \
935 934 .filter(UserEmailMap.user == c.user).all()
936 935
937 936 return self._get_template_context(c)
938 937
939 938 @LoginRequired()
940 939 @HasPermissionAllDecorator('hg.admin')
941 940 @CSRFRequired()
942 941 @view_config(
943 942 route_name='edit_user_emails_add', request_method='POST')
944 943 def emails_add(self):
945 944 _ = self.request.translate
946 945 c = self.load_default_context()
947 946
948 947 user_id = self.db_user_id
949 948 c.user = self.db_user
950 949
951 950 email = self.request.POST.get('new_email')
952 951 user_data = c.user.get_api_data()
953 952 try:
954 953
955 954 form = UserExtraEmailForm(self.request.translate)()
956 955 data = form.to_python({'email': email})
957 956 email = data['email']
958 957
959 958 UserModel().add_extra_email(c.user.user_id, email)
960 959 audit_logger.store_web(
961 960 'user.edit.email.add',
962 961 action_data={'email': email, 'user': user_data},
963 962 user=self._rhodecode_user)
964 963 Session().commit()
965 964 h.flash(_("Added new email address `%s` for user account") % email,
966 965 category='success')
967 966 except formencode.Invalid as error:
968 967 h.flash(h.escape(error.error_dict['email']), category='error')
969 968 except IntegrityError:
970 969 log.warning("Email %s already exists", email)
971 970 h.flash(_('Email `{}` is already registered for another user.').format(email),
972 971 category='error')
973 972 except Exception:
974 973 log.exception("Exception during email saving")
975 974 h.flash(_('An error occurred during email saving'),
976 975 category='error')
977 976 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
978 977
979 978 @LoginRequired()
980 979 @HasPermissionAllDecorator('hg.admin')
981 980 @CSRFRequired()
982 981 @view_config(
983 982 route_name='edit_user_emails_delete', request_method='POST')
984 983 def emails_delete(self):
985 984 _ = self.request.translate
986 985 c = self.load_default_context()
987 986
988 987 user_id = self.db_user_id
989 988 c.user = self.db_user
990 989
991 990 email_id = self.request.POST.get('del_email_id')
992 991 user_model = UserModel()
993 992
994 993 email = UserEmailMap.query().get(email_id).email
995 994 user_data = c.user.get_api_data()
996 995 user_model.delete_extra_email(c.user.user_id, email_id)
997 996 audit_logger.store_web(
998 997 'user.edit.email.delete',
999 998 action_data={'email': email, 'user': user_data},
1000 999 user=self._rhodecode_user)
1001 1000 Session().commit()
1002 1001 h.flash(_("Removed email address from user account"),
1003 1002 category='success')
1004 1003 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
1005 1004
1006 1005 @LoginRequired()
1007 1006 @HasPermissionAllDecorator('hg.admin')
1008 1007 @view_config(
1009 1008 route_name='edit_user_ips', request_method='GET',
1010 1009 renderer='rhodecode:templates/admin/users/user_edit.mako')
1011 1010 def ips(self):
1012 1011 _ = self.request.translate
1013 1012 c = self.load_default_context()
1014 1013 c.user = self.db_user
1015 1014
1016 1015 c.active = 'ips'
1017 1016 c.user_ip_map = UserIpMap.query() \
1018 1017 .filter(UserIpMap.user == c.user).all()
1019 1018
1020 1019 c.inherit_default_ips = c.user.inherit_default_permissions
1021 1020 c.default_user_ip_map = UserIpMap.query() \
1022 1021 .filter(UserIpMap.user == User.get_default_user()).all()
1023 1022
1024 1023 return self._get_template_context(c)
1025 1024
1026 1025 @LoginRequired()
1027 1026 @HasPermissionAllDecorator('hg.admin')
1028 1027 @CSRFRequired()
1029 1028 @view_config(
1030 1029 route_name='edit_user_ips_add', request_method='POST')
1031 1030 # NOTE(marcink): this view is allowed for default users, as we can
1032 1031 # edit their IP white list
1033 1032 def ips_add(self):
1034 1033 _ = self.request.translate
1035 1034 c = self.load_default_context()
1036 1035
1037 1036 user_id = self.db_user_id
1038 1037 c.user = self.db_user
1039 1038
1040 1039 user_model = UserModel()
1041 1040 desc = self.request.POST.get('description')
1042 1041 try:
1043 1042 ip_list = user_model.parse_ip_range(
1044 1043 self.request.POST.get('new_ip'))
1045 1044 except Exception as e:
1046 1045 ip_list = []
1047 1046 log.exception("Exception during ip saving")
1048 1047 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1049 1048 category='error')
1050 1049 added = []
1051 1050 user_data = c.user.get_api_data()
1052 1051 for ip in ip_list:
1053 1052 try:
1054 1053 form = UserExtraIpForm(self.request.translate)()
1055 1054 data = form.to_python({'ip': ip})
1056 1055 ip = data['ip']
1057 1056
1058 1057 user_model.add_extra_ip(c.user.user_id, ip, desc)
1059 1058 audit_logger.store_web(
1060 1059 'user.edit.ip.add',
1061 1060 action_data={'ip': ip, 'user': user_data},
1062 1061 user=self._rhodecode_user)
1063 1062 Session().commit()
1064 1063 added.append(ip)
1065 1064 except formencode.Invalid as error:
1066 1065 msg = error.error_dict['ip']
1067 1066 h.flash(msg, category='error')
1068 1067 except Exception:
1069 1068 log.exception("Exception during ip saving")
1070 1069 h.flash(_('An error occurred during ip saving'),
1071 1070 category='error')
1072 1071 if added:
1073 1072 h.flash(
1074 1073 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1075 1074 category='success')
1076 1075 if 'default_user' in self.request.POST:
1077 1076 # case for editing global IP list we do it for 'DEFAULT' user
1078 1077 raise HTTPFound(h.route_path('admin_permissions_ips'))
1079 1078 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1080 1079
1081 1080 @LoginRequired()
1082 1081 @HasPermissionAllDecorator('hg.admin')
1083 1082 @CSRFRequired()
1084 1083 @view_config(
1085 1084 route_name='edit_user_ips_delete', request_method='POST')
1086 1085 # NOTE(marcink): this view is allowed for default users, as we can
1087 1086 # edit their IP white list
1088 1087 def ips_delete(self):
1089 1088 _ = self.request.translate
1090 1089 c = self.load_default_context()
1091 1090
1092 1091 user_id = self.db_user_id
1093 1092 c.user = self.db_user
1094 1093
1095 1094 ip_id = self.request.POST.get('del_ip_id')
1096 1095 user_model = UserModel()
1097 1096 user_data = c.user.get_api_data()
1098 1097 ip = UserIpMap.query().get(ip_id).ip_addr
1099 1098 user_model.delete_extra_ip(c.user.user_id, ip_id)
1100 1099 audit_logger.store_web(
1101 1100 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1102 1101 user=self._rhodecode_user)
1103 1102 Session().commit()
1104 1103 h.flash(_("Removed ip address from user whitelist"), category='success')
1105 1104
1106 1105 if 'default_user' in self.request.POST:
1107 1106 # case for editing global IP list we do it for 'DEFAULT' user
1108 1107 raise HTTPFound(h.route_path('admin_permissions_ips'))
1109 1108 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1110 1109
1111 1110 @LoginRequired()
1112 1111 @HasPermissionAllDecorator('hg.admin')
1113 1112 @view_config(
1114 1113 route_name='edit_user_groups_management', request_method='GET',
1115 1114 renderer='rhodecode:templates/admin/users/user_edit.mako')
1116 1115 def groups_management(self):
1117 1116 c = self.load_default_context()
1118 1117 c.user = self.db_user
1119 1118 c.data = c.user.group_member
1120 1119
1121 1120 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1122 1121 for group in c.user.group_member]
1123 1122 c.groups = json.dumps(groups)
1124 1123 c.active = 'groups'
1125 1124
1126 1125 return self._get_template_context(c)
1127 1126
1128 1127 @LoginRequired()
1129 1128 @HasPermissionAllDecorator('hg.admin')
1130 1129 @CSRFRequired()
1131 1130 @view_config(
1132 1131 route_name='edit_user_groups_management_updates', request_method='POST')
1133 1132 def groups_management_updates(self):
1134 1133 _ = self.request.translate
1135 1134 c = self.load_default_context()
1136 1135
1137 1136 user_id = self.db_user_id
1138 1137 c.user = self.db_user
1139 1138
1140 1139 user_groups = set(self.request.POST.getall('users_group_id'))
1141 1140 user_groups_objects = []
1142 1141
1143 1142 for ugid in user_groups:
1144 1143 user_groups_objects.append(
1145 1144 UserGroupModel().get_group(safe_int(ugid)))
1146 1145 user_group_model = UserGroupModel()
1147 1146 added_to_groups, removed_from_groups = \
1148 1147 user_group_model.change_groups(c.user, user_groups_objects)
1149 1148
1150 1149 user_data = c.user.get_api_data()
1151 1150 for user_group_id in added_to_groups:
1152 1151 user_group = UserGroup.get(user_group_id)
1153 1152 old_values = user_group.get_api_data()
1154 1153 audit_logger.store_web(
1155 1154 'user_group.edit.member.add',
1156 1155 action_data={'user': user_data, 'old_data': old_values},
1157 1156 user=self._rhodecode_user)
1158 1157
1159 1158 for user_group_id in removed_from_groups:
1160 1159 user_group = UserGroup.get(user_group_id)
1161 1160 old_values = user_group.get_api_data()
1162 1161 audit_logger.store_web(
1163 1162 'user_group.edit.member.delete',
1164 1163 action_data={'user': user_data, 'old_data': old_values},
1165 1164 user=self._rhodecode_user)
1166 1165
1167 1166 Session().commit()
1168 1167 c.active = 'user_groups_management'
1169 1168 h.flash(_("Groups successfully changed"), category='success')
1170 1169
1171 1170 return HTTPFound(h.route_path(
1172 1171 'edit_user_groups_management', user_id=user_id))
1173 1172
1174 1173 @LoginRequired()
1175 1174 @HasPermissionAllDecorator('hg.admin')
1176 1175 @view_config(
1177 1176 route_name='edit_user_audit_logs', request_method='GET',
1178 1177 renderer='rhodecode:templates/admin/users/user_edit.mako')
1179 1178 def user_audit_logs(self):
1180 1179 _ = self.request.translate
1181 1180 c = self.load_default_context()
1182 1181 c.user = self.db_user
1183 1182
1184 1183 c.active = 'audit'
1185 1184
1186 1185 p = safe_int(self.request.GET.get('page', 1), 1)
1187 1186
1188 1187 filter_term = self.request.GET.get('filter')
1189 1188 user_log = UserModel().get_user_log(c.user, filter_term)
1190 1189
1191 1190 def url_generator(**kw):
1192 1191 if filter_term:
1193 1192 kw['filter'] = filter_term
1194 1193 return self.request.current_route_path(_query=kw)
1195 1194
1196 1195 c.audit_logs = h.Page(
1197 1196 user_log, page=p, items_per_page=10, url=url_generator)
1198 1197 c.filter_term = filter_term
1199 1198 return self._get_template_context(c)
1200 1199
1201 1200 @LoginRequired()
1202 1201 @HasPermissionAllDecorator('hg.admin')
1203 1202 @view_config(
1204 1203 route_name='edit_user_perms_summary', request_method='GET',
1205 1204 renderer='rhodecode:templates/admin/users/user_edit.mako')
1206 1205 def user_perms_summary(self):
1207 1206 _ = self.request.translate
1208 1207 c = self.load_default_context()
1209 1208 c.user = self.db_user
1210 1209
1211 1210 c.active = 'perms_summary'
1212 1211 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1213 1212
1214 1213 return self._get_template_context(c)
1215 1214
1216 1215 @LoginRequired()
1217 1216 @HasPermissionAllDecorator('hg.admin')
1218 1217 @view_config(
1219 1218 route_name='edit_user_perms_summary_json', request_method='GET',
1220 1219 renderer='json_ext')
1221 1220 def user_perms_summary_json(self):
1222 1221 self.load_default_context()
1223 1222 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1224 1223
1225 1224 return perm_user.permissions
1226 1225
1227 1226 @LoginRequired()
1228 1227 @HasPermissionAllDecorator('hg.admin')
1229 1228 @view_config(
1230 1229 route_name='edit_user_caches', request_method='GET',
1231 1230 renderer='rhodecode:templates/admin/users/user_edit.mako')
1232 1231 def user_caches(self):
1233 1232 _ = self.request.translate
1234 1233 c = self.load_default_context()
1235 1234 c.user = self.db_user
1236 1235
1237 1236 c.active = 'caches'
1238 1237 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1239 1238
1240 1239 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1241 1240 c.region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1242 1241 c.backend = c.region.backend
1243 1242 c.user_keys = sorted(c.region.backend.list_keys(prefix=cache_namespace_uid))
1244 1243
1245 1244 return self._get_template_context(c)
1246 1245
1247 1246 @LoginRequired()
1248 1247 @HasPermissionAllDecorator('hg.admin')
1249 1248 @CSRFRequired()
1250 1249 @view_config(
1251 1250 route_name='edit_user_caches_update', request_method='POST')
1252 1251 def user_caches_update(self):
1253 1252 _ = self.request.translate
1254 1253 c = self.load_default_context()
1255 1254 c.user = self.db_user
1256 1255
1257 1256 c.active = 'caches'
1258 1257 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1259 1258
1260 1259 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1261 1260 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid)
1262 1261
1263 1262 h.flash(_("Deleted {} cache keys").format(del_keys), category='success')
1264 1263
1265 1264 return HTTPFound(h.route_path(
1266 1265 'edit_user_caches', user_id=c.user.user_id))
@@ -1,777 +1,779 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
22 22 """
23 23 repo group model for RhodeCode
24 24 """
25 25
26 26 import os
27 27 import datetime
28 28 import itertools
29 29 import logging
30 30 import shutil
31 31 import traceback
32 32 import string
33 33
34 34 from zope.cachedescriptors.property import Lazy as LazyProperty
35 35
36 36 from rhodecode import events
37 37 from rhodecode.model import BaseModel
38 38 from rhodecode.model.db import (_hash_key,
39 39 RepoGroup, UserRepoGroupToPerm, User, Permission, UserGroupRepoGroupToPerm,
40 40 UserGroup, Repository)
41 41 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
42 42 from rhodecode.lib.caching_query import FromCache
43 43 from rhodecode.lib.utils2 import action_logger_generic, datetime_to_time
44 44
45 45 log = logging.getLogger(__name__)
46 46
47 47
48 48 class RepoGroupModel(BaseModel):
49 49
50 50 cls = RepoGroup
51 51 PERSONAL_GROUP_DESC = 'personal repo group of user `%(username)s`'
52 52 PERSONAL_GROUP_PATTERN = '${username}' # default
53 53
54 54 def _get_user_group(self, users_group):
55 55 return self._get_instance(UserGroup, users_group,
56 56 callback=UserGroup.get_by_group_name)
57 57
58 58 def _get_repo_group(self, repo_group):
59 59 return self._get_instance(RepoGroup, repo_group,
60 60 callback=RepoGroup.get_by_group_name)
61 61
62 62 @LazyProperty
63 63 def repos_path(self):
64 64 """
65 65 Gets the repositories root path from database
66 66 """
67 67
68 68 settings_model = VcsSettingsModel(sa=self.sa)
69 69 return settings_model.get_repos_location()
70 70
71 71 def get_by_group_name(self, repo_group_name, cache=None):
72 72 repo = self.sa.query(RepoGroup) \
73 73 .filter(RepoGroup.group_name == repo_group_name)
74 74
75 75 if cache:
76 76 name_key = _hash_key(repo_group_name)
77 77 repo = repo.options(
78 78 FromCache("sql_cache_short", "get_repo_group_%s" % name_key))
79 79 return repo.scalar()
80 80
81 81 def get_default_create_personal_repo_group(self):
82 82 value = SettingsModel().get_setting_by_name(
83 83 'create_personal_repo_group')
84 84 return value.app_settings_value if value else None or False
85 85
86 86 def get_personal_group_name_pattern(self):
87 87 value = SettingsModel().get_setting_by_name(
88 88 'personal_repo_group_pattern')
89 89 val = value.app_settings_value if value else None
90 90 group_template = val or self.PERSONAL_GROUP_PATTERN
91 91
92 92 group_template = group_template.lstrip('/')
93 93 return group_template
94 94
95 95 def get_personal_group_name(self, user):
96 96 template = self.get_personal_group_name_pattern()
97 97 return string.Template(template).safe_substitute(
98 98 username=user.username,
99 99 user_id=user.user_id,
100 first_name=user.first_name,
101 last_name=user.last_name,
100 102 )
101 103
102 104 def create_personal_repo_group(self, user, commit_early=True):
103 105 desc = self.PERSONAL_GROUP_DESC % {'username': user.username}
104 106 personal_repo_group_name = self.get_personal_group_name(user)
105 107
106 108 # create a new one
107 109 RepoGroupModel().create(
108 110 group_name=personal_repo_group_name,
109 111 group_description=desc,
110 112 owner=user.username,
111 113 personal=True,
112 114 commit_early=commit_early)
113 115
114 116 def _create_default_perms(self, new_group):
115 117 # create default permission
116 118 default_perm = 'group.read'
117 119 def_user = User.get_default_user()
118 120 for p in def_user.user_perms:
119 121 if p.permission.permission_name.startswith('group.'):
120 122 default_perm = p.permission.permission_name
121 123 break
122 124
123 125 repo_group_to_perm = UserRepoGroupToPerm()
124 126 repo_group_to_perm.permission = Permission.get_by_key(default_perm)
125 127
126 128 repo_group_to_perm.group = new_group
127 129 repo_group_to_perm.user_id = def_user.user_id
128 130 return repo_group_to_perm
129 131
130 132 def _get_group_name_and_parent(self, group_name_full, repo_in_path=False,
131 133 get_object=False):
132 134 """
133 135 Get's the group name and a parent group name from given group name.
134 136 If repo_in_path is set to truth, we asume the full path also includes
135 137 repo name, in such case we clean the last element.
136 138
137 139 :param group_name_full:
138 140 """
139 141 split_paths = 1
140 142 if repo_in_path:
141 143 split_paths = 2
142 144 _parts = group_name_full.rsplit(RepoGroup.url_sep(), split_paths)
143 145
144 146 if repo_in_path and len(_parts) > 1:
145 147 # such case last element is the repo_name
146 148 _parts.pop(-1)
147 149 group_name_cleaned = _parts[-1] # just the group name
148 150 parent_repo_group_name = None
149 151
150 152 if len(_parts) > 1:
151 153 parent_repo_group_name = _parts[0]
152 154
153 155 parent_group = None
154 156 if parent_repo_group_name:
155 157 parent_group = RepoGroup.get_by_group_name(parent_repo_group_name)
156 158
157 159 if get_object:
158 160 return group_name_cleaned, parent_repo_group_name, parent_group
159 161
160 162 return group_name_cleaned, parent_repo_group_name
161 163
162 164 def check_exist_filesystem(self, group_name, exc_on_failure=True):
163 165 create_path = os.path.join(self.repos_path, group_name)
164 166 log.debug('creating new group in %s', create_path)
165 167
166 168 if os.path.isdir(create_path):
167 169 if exc_on_failure:
168 170 abs_create_path = os.path.abspath(create_path)
169 171 raise Exception('Directory `{}` already exists !'.format(abs_create_path))
170 172 return False
171 173 return True
172 174
173 175 def _create_group(self, group_name):
174 176 """
175 177 makes repository group on filesystem
176 178
177 179 :param repo_name:
178 180 :param parent_id:
179 181 """
180 182
181 183 self.check_exist_filesystem(group_name)
182 184 create_path = os.path.join(self.repos_path, group_name)
183 185 log.debug('creating new group in %s', create_path)
184 186 os.makedirs(create_path, mode=0o755)
185 187 log.debug('created group in %s', create_path)
186 188
187 189 def _rename_group(self, old, new):
188 190 """
189 191 Renames a group on filesystem
190 192
191 193 :param group_name:
192 194 """
193 195
194 196 if old == new:
195 197 log.debug('skipping group rename')
196 198 return
197 199
198 200 log.debug('renaming repository group from %s to %s', old, new)
199 201
200 202 old_path = os.path.join(self.repos_path, old)
201 203 new_path = os.path.join(self.repos_path, new)
202 204
203 205 log.debug('renaming repos paths from %s to %s', old_path, new_path)
204 206
205 207 if os.path.isdir(new_path):
206 208 raise Exception('Was trying to rename to already '
207 209 'existing dir %s' % new_path)
208 210 shutil.move(old_path, new_path)
209 211
210 212 def _delete_filesystem_group(self, group, force_delete=False):
211 213 """
212 214 Deletes a group from a filesystem
213 215
214 216 :param group: instance of group from database
215 217 :param force_delete: use shutil rmtree to remove all objects
216 218 """
217 219 paths = group.full_path.split(RepoGroup.url_sep())
218 220 paths = os.sep.join(paths)
219 221
220 222 rm_path = os.path.join(self.repos_path, paths)
221 223 log.info("Removing group %s", rm_path)
222 224 # delete only if that path really exists
223 225 if os.path.isdir(rm_path):
224 226 if force_delete:
225 227 shutil.rmtree(rm_path)
226 228 else:
227 229 # archive that group`
228 230 _now = datetime.datetime.now()
229 231 _ms = str(_now.microsecond).rjust(6, '0')
230 232 _d = 'rm__%s_GROUP_%s' % (
231 233 _now.strftime('%Y%m%d_%H%M%S_' + _ms), group.name)
232 234 shutil.move(rm_path, os.path.join(self.repos_path, _d))
233 235
234 236 def create(self, group_name, group_description, owner, just_db=False,
235 237 copy_permissions=False, personal=None, commit_early=True):
236 238
237 239 (group_name_cleaned,
238 240 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(group_name)
239 241
240 242 parent_group = None
241 243 if parent_group_name:
242 244 parent_group = self._get_repo_group(parent_group_name)
243 245 if not parent_group:
244 246 # we tried to create a nested group, but the parent is not
245 247 # existing
246 248 raise ValueError(
247 249 'Parent group `%s` given in `%s` group name '
248 250 'is not yet existing.' % (parent_group_name, group_name))
249 251
250 252 # because we are doing a cleanup, we need to check if such directory
251 253 # already exists. If we don't do that we can accidentally delete
252 254 # existing directory via cleanup that can cause data issues, since
253 255 # delete does a folder rename to special syntax later cleanup
254 256 # functions can delete this
255 257 cleanup_group = self.check_exist_filesystem(group_name,
256 258 exc_on_failure=False)
257 259 user = self._get_user(owner)
258 260 if not user:
259 261 raise ValueError('Owner %s not found as rhodecode user', owner)
260 262
261 263 try:
262 264 new_repo_group = RepoGroup()
263 265 new_repo_group.user = user
264 266 new_repo_group.group_description = group_description or group_name
265 267 new_repo_group.parent_group = parent_group
266 268 new_repo_group.group_name = group_name
267 269 new_repo_group.personal = personal
268 270
269 271 self.sa.add(new_repo_group)
270 272
271 273 # create an ADMIN permission for owner except if we're super admin,
272 274 # later owner should go into the owner field of groups
273 275 if not user.is_admin:
274 276 self.grant_user_permission(repo_group=new_repo_group,
275 277 user=owner, perm='group.admin')
276 278
277 279 if parent_group and copy_permissions:
278 280 # copy permissions from parent
279 281 user_perms = UserRepoGroupToPerm.query() \
280 282 .filter(UserRepoGroupToPerm.group == parent_group).all()
281 283
282 284 group_perms = UserGroupRepoGroupToPerm.query() \
283 285 .filter(UserGroupRepoGroupToPerm.group == parent_group).all()
284 286
285 287 for perm in user_perms:
286 288 # don't copy over the permission for user who is creating
287 289 # this group, if he is not super admin he get's admin
288 290 # permission set above
289 291 if perm.user != user or user.is_admin:
290 292 UserRepoGroupToPerm.create(
291 293 perm.user, new_repo_group, perm.permission)
292 294
293 295 for perm in group_perms:
294 296 UserGroupRepoGroupToPerm.create(
295 297 perm.users_group, new_repo_group, perm.permission)
296 298 else:
297 299 perm_obj = self._create_default_perms(new_repo_group)
298 300 self.sa.add(perm_obj)
299 301
300 302 # now commit the changes, earlier so we are sure everything is in
301 303 # the database.
302 304 if commit_early:
303 305 self.sa.commit()
304 306 if not just_db:
305 307 self._create_group(new_repo_group.group_name)
306 308
307 309 # trigger the post hook
308 310 from rhodecode.lib.hooks_base import log_create_repository_group
309 311 repo_group = RepoGroup.get_by_group_name(group_name)
310 312 log_create_repository_group(
311 313 created_by=user.username, **repo_group.get_dict())
312 314
313 315 # Trigger create event.
314 316 events.trigger(events.RepoGroupCreateEvent(repo_group))
315 317
316 318 return new_repo_group
317 319 except Exception:
318 320 self.sa.rollback()
319 321 log.exception('Exception occurred when creating repository group, '
320 322 'doing cleanup...')
321 323 # rollback things manually !
322 324 repo_group = RepoGroup.get_by_group_name(group_name)
323 325 if repo_group:
324 326 RepoGroup.delete(repo_group.group_id)
325 327 self.sa.commit()
326 328 if cleanup_group:
327 329 RepoGroupModel()._delete_filesystem_group(repo_group)
328 330 raise
329 331
330 332 def update_permissions(
331 333 self, repo_group, perm_additions=None, perm_updates=None,
332 334 perm_deletions=None, recursive=None, check_perms=True,
333 335 cur_user=None):
334 336 from rhodecode.model.repo import RepoModel
335 337 from rhodecode.lib.auth import HasUserGroupPermissionAny
336 338
337 339 if not perm_additions:
338 340 perm_additions = []
339 341 if not perm_updates:
340 342 perm_updates = []
341 343 if not perm_deletions:
342 344 perm_deletions = []
343 345
344 346 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
345 347
346 348 changes = {
347 349 'added': [],
348 350 'updated': [],
349 351 'deleted': []
350 352 }
351 353
352 354 def _set_perm_user(obj, user, perm):
353 355 if isinstance(obj, RepoGroup):
354 356 self.grant_user_permission(
355 357 repo_group=obj, user=user, perm=perm)
356 358 elif isinstance(obj, Repository):
357 359 # private repos will not allow to change the default
358 360 # permissions using recursive mode
359 361 if obj.private and user == User.DEFAULT_USER:
360 362 return
361 363
362 364 # we set group permission but we have to switch to repo
363 365 # permission
364 366 perm = perm.replace('group.', 'repository.')
365 367 RepoModel().grant_user_permission(
366 368 repo=obj, user=user, perm=perm)
367 369
368 370 def _set_perm_group(obj, users_group, perm):
369 371 if isinstance(obj, RepoGroup):
370 372 self.grant_user_group_permission(
371 373 repo_group=obj, group_name=users_group, perm=perm)
372 374 elif isinstance(obj, Repository):
373 375 # we set group permission but we have to switch to repo
374 376 # permission
375 377 perm = perm.replace('group.', 'repository.')
376 378 RepoModel().grant_user_group_permission(
377 379 repo=obj, group_name=users_group, perm=perm)
378 380
379 381 def _revoke_perm_user(obj, user):
380 382 if isinstance(obj, RepoGroup):
381 383 self.revoke_user_permission(repo_group=obj, user=user)
382 384 elif isinstance(obj, Repository):
383 385 RepoModel().revoke_user_permission(repo=obj, user=user)
384 386
385 387 def _revoke_perm_group(obj, user_group):
386 388 if isinstance(obj, RepoGroup):
387 389 self.revoke_user_group_permission(
388 390 repo_group=obj, group_name=user_group)
389 391 elif isinstance(obj, Repository):
390 392 RepoModel().revoke_user_group_permission(
391 393 repo=obj, group_name=user_group)
392 394
393 395 # start updates
394 396 log.debug('Now updating permissions for %s in recursive mode:%s',
395 397 repo_group, recursive)
396 398
397 399 # initialize check function, we'll call that multiple times
398 400 has_group_perm = HasUserGroupPermissionAny(*req_perms)
399 401
400 402 for obj in repo_group.recursive_groups_and_repos():
401 403 # iterated obj is an instance of a repos group or repository in
402 404 # that group, recursive option can be: none, repos, groups, all
403 405 if recursive == 'all':
404 406 obj = obj
405 407 elif recursive == 'repos':
406 408 # skip groups, other than this one
407 409 if isinstance(obj, RepoGroup) and not obj == repo_group:
408 410 continue
409 411 elif recursive == 'groups':
410 412 # skip repos
411 413 if isinstance(obj, Repository):
412 414 continue
413 415 else: # recursive == 'none':
414 416 # DEFAULT option - don't apply to iterated objects
415 417 # also we do a break at the end of this loop. if we are not
416 418 # in recursive mode
417 419 obj = repo_group
418 420
419 421 change_obj = obj.get_api_data()
420 422
421 423 # update permissions
422 424 for member_id, perm, member_type in perm_updates:
423 425 member_id = int(member_id)
424 426 if member_type == 'user':
425 427 member_name = User.get(member_id).username
426 428 # this updates also current one if found
427 429 _set_perm_user(obj, user=member_id, perm=perm)
428 430 elif member_type == 'user_group':
429 431 member_name = UserGroup.get(member_id).users_group_name
430 432 if not check_perms or has_group_perm(member_name,
431 433 user=cur_user):
432 434 _set_perm_group(obj, users_group=member_id, perm=perm)
433 435 else:
434 436 raise ValueError("member_type must be 'user' or 'user_group' "
435 437 "got {} instead".format(member_type))
436 438
437 439 changes['updated'].append(
438 440 {'change_obj': change_obj, 'type': member_type,
439 441 'id': member_id, 'name': member_name, 'new_perm': perm})
440 442
441 443 # set new permissions
442 444 for member_id, perm, member_type in perm_additions:
443 445 member_id = int(member_id)
444 446 if member_type == 'user':
445 447 member_name = User.get(member_id).username
446 448 _set_perm_user(obj, user=member_id, perm=perm)
447 449 elif member_type == 'user_group':
448 450 # check if we have permissions to alter this usergroup
449 451 member_name = UserGroup.get(member_id).users_group_name
450 452 if not check_perms or has_group_perm(member_name,
451 453 user=cur_user):
452 454 _set_perm_group(obj, users_group=member_id, perm=perm)
453 455 else:
454 456 raise ValueError("member_type must be 'user' or 'user_group' "
455 457 "got {} instead".format(member_type))
456 458
457 459 changes['added'].append(
458 460 {'change_obj': change_obj, 'type': member_type,
459 461 'id': member_id, 'name': member_name, 'new_perm': perm})
460 462
461 463 # delete permissions
462 464 for member_id, perm, member_type in perm_deletions:
463 465 member_id = int(member_id)
464 466 if member_type == 'user':
465 467 member_name = User.get(member_id).username
466 468 _revoke_perm_user(obj, user=member_id)
467 469 elif member_type == 'user_group':
468 470 # check if we have permissions to alter this usergroup
469 471 member_name = UserGroup.get(member_id).users_group_name
470 472 if not check_perms or has_group_perm(member_name,
471 473 user=cur_user):
472 474 _revoke_perm_group(obj, user_group=member_id)
473 475 else:
474 476 raise ValueError("member_type must be 'user' or 'user_group' "
475 477 "got {} instead".format(member_type))
476 478
477 479 changes['deleted'].append(
478 480 {'change_obj': change_obj, 'type': member_type,
479 481 'id': member_id, 'name': member_name, 'new_perm': perm})
480 482
481 483 # if it's not recursive call for all,repos,groups
482 484 # break the loop and don't proceed with other changes
483 485 if recursive not in ['all', 'repos', 'groups']:
484 486 break
485 487
486 488 return changes
487 489
488 490 def update(self, repo_group, form_data):
489 491 try:
490 492 repo_group = self._get_repo_group(repo_group)
491 493 old_path = repo_group.full_path
492 494
493 495 # change properties
494 496 if 'group_description' in form_data:
495 497 repo_group.group_description = form_data['group_description']
496 498
497 499 if 'enable_locking' in form_data:
498 500 repo_group.enable_locking = form_data['enable_locking']
499 501
500 502 if 'group_parent_id' in form_data:
501 503 parent_group = (
502 504 self._get_repo_group(form_data['group_parent_id']))
503 505 repo_group.group_parent_id = (
504 506 parent_group.group_id if parent_group else None)
505 507 repo_group.parent_group = parent_group
506 508
507 509 # mikhail: to update the full_path, we have to explicitly
508 510 # update group_name
509 511 group_name = form_data.get('group_name', repo_group.name)
510 512 repo_group.group_name = repo_group.get_new_name(group_name)
511 513
512 514 new_path = repo_group.full_path
513 515
514 516 if 'user' in form_data:
515 517 repo_group.user = User.get_by_username(form_data['user'])
516 518 repo_group.updated_on = datetime.datetime.now()
517 519 self.sa.add(repo_group)
518 520
519 521 # iterate over all members of this groups and do fixes
520 522 # set locking if given
521 523 # if obj is a repoGroup also fix the name of the group according
522 524 # to the parent
523 525 # if obj is a Repo fix it's name
524 526 # this can be potentially heavy operation
525 527 for obj in repo_group.recursive_groups_and_repos():
526 528 # set the value from it's parent
527 529 obj.enable_locking = repo_group.enable_locking
528 530 if isinstance(obj, RepoGroup):
529 531 new_name = obj.get_new_name(obj.name)
530 532 log.debug('Fixing group %s to new name %s',
531 533 obj.group_name, new_name)
532 534 obj.group_name = new_name
533 535 obj.updated_on = datetime.datetime.now()
534 536 elif isinstance(obj, Repository):
535 537 # we need to get all repositories from this new group and
536 538 # rename them accordingly to new group path
537 539 new_name = obj.get_new_name(obj.just_name)
538 540 log.debug('Fixing repo %s to new name %s',
539 541 obj.repo_name, new_name)
540 542 obj.repo_name = new_name
541 543 obj.updated_on = datetime.datetime.now()
542 544 self.sa.add(obj)
543 545
544 546 self._rename_group(old_path, new_path)
545 547
546 548 # Trigger update event.
547 549 events.trigger(events.RepoGroupUpdateEvent(repo_group))
548 550
549 551 return repo_group
550 552 except Exception:
551 553 log.error(traceback.format_exc())
552 554 raise
553 555
554 556 def delete(self, repo_group, force_delete=False, fs_remove=True):
555 557 repo_group = self._get_repo_group(repo_group)
556 558 if not repo_group:
557 559 return False
558 560 try:
559 561 self.sa.delete(repo_group)
560 562 if fs_remove:
561 563 self._delete_filesystem_group(repo_group, force_delete)
562 564 else:
563 565 log.debug('skipping removal from filesystem')
564 566
565 567 # Trigger delete event.
566 568 events.trigger(events.RepoGroupDeleteEvent(repo_group))
567 569 return True
568 570
569 571 except Exception:
570 572 log.error('Error removing repo_group %s', repo_group)
571 573 raise
572 574
573 575 def grant_user_permission(self, repo_group, user, perm):
574 576 """
575 577 Grant permission for user on given repository group, or update
576 578 existing one if found
577 579
578 580 :param repo_group: Instance of RepoGroup, repositories_group_id,
579 581 or repositories_group name
580 582 :param user: Instance of User, user_id or username
581 583 :param perm: Instance of Permission, or permission_name
582 584 """
583 585
584 586 repo_group = self._get_repo_group(repo_group)
585 587 user = self._get_user(user)
586 588 permission = self._get_perm(perm)
587 589
588 590 # check if we have that permission already
589 591 obj = self.sa.query(UserRepoGroupToPerm)\
590 592 .filter(UserRepoGroupToPerm.user == user)\
591 593 .filter(UserRepoGroupToPerm.group == repo_group)\
592 594 .scalar()
593 595 if obj is None:
594 596 # create new !
595 597 obj = UserRepoGroupToPerm()
596 598 obj.group = repo_group
597 599 obj.user = user
598 600 obj.permission = permission
599 601 self.sa.add(obj)
600 602 log.debug('Granted perm %s to %s on %s', perm, user, repo_group)
601 603 action_logger_generic(
602 604 'granted permission: {} to user: {} on repogroup: {}'.format(
603 605 perm, user, repo_group), namespace='security.repogroup')
604 606 return obj
605 607
606 608 def revoke_user_permission(self, repo_group, user):
607 609 """
608 610 Revoke permission for user on given repository group
609 611
610 612 :param repo_group: Instance of RepoGroup, repositories_group_id,
611 613 or repositories_group name
612 614 :param user: Instance of User, user_id or username
613 615 """
614 616
615 617 repo_group = self._get_repo_group(repo_group)
616 618 user = self._get_user(user)
617 619
618 620 obj = self.sa.query(UserRepoGroupToPerm)\
619 621 .filter(UserRepoGroupToPerm.user == user)\
620 622 .filter(UserRepoGroupToPerm.group == repo_group)\
621 623 .scalar()
622 624 if obj:
623 625 self.sa.delete(obj)
624 626 log.debug('Revoked perm on %s on %s', repo_group, user)
625 627 action_logger_generic(
626 628 'revoked permission from user: {} on repogroup: {}'.format(
627 629 user, repo_group), namespace='security.repogroup')
628 630
629 631 def grant_user_group_permission(self, repo_group, group_name, perm):
630 632 """
631 633 Grant permission for user group on given repository group, or update
632 634 existing one if found
633 635
634 636 :param repo_group: Instance of RepoGroup, repositories_group_id,
635 637 or repositories_group name
636 638 :param group_name: Instance of UserGroup, users_group_id,
637 639 or user group name
638 640 :param perm: Instance of Permission, or permission_name
639 641 """
640 642 repo_group = self._get_repo_group(repo_group)
641 643 group_name = self._get_user_group(group_name)
642 644 permission = self._get_perm(perm)
643 645
644 646 # check if we have that permission already
645 647 obj = self.sa.query(UserGroupRepoGroupToPerm)\
646 648 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
647 649 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
648 650 .scalar()
649 651
650 652 if obj is None:
651 653 # create new
652 654 obj = UserGroupRepoGroupToPerm()
653 655
654 656 obj.group = repo_group
655 657 obj.users_group = group_name
656 658 obj.permission = permission
657 659 self.sa.add(obj)
658 660 log.debug('Granted perm %s to %s on %s', perm, group_name, repo_group)
659 661 action_logger_generic(
660 662 'granted permission: {} to usergroup: {} on repogroup: {}'.format(
661 663 perm, group_name, repo_group), namespace='security.repogroup')
662 664 return obj
663 665
664 666 def revoke_user_group_permission(self, repo_group, group_name):
665 667 """
666 668 Revoke permission for user group on given repository group
667 669
668 670 :param repo_group: Instance of RepoGroup, repositories_group_id,
669 671 or repositories_group name
670 672 :param group_name: Instance of UserGroup, users_group_id,
671 673 or user group name
672 674 """
673 675 repo_group = self._get_repo_group(repo_group)
674 676 group_name = self._get_user_group(group_name)
675 677
676 678 obj = self.sa.query(UserGroupRepoGroupToPerm)\
677 679 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
678 680 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
679 681 .scalar()
680 682 if obj:
681 683 self.sa.delete(obj)
682 684 log.debug('Revoked perm to %s on %s', repo_group, group_name)
683 685 action_logger_generic(
684 686 'revoked permission from usergroup: {} on repogroup: {}'.format(
685 687 group_name, repo_group), namespace='security.repogroup')
686 688
687 689 def get_repo_groups_as_dict(self, repo_group_list=None, admin=False,
688 690 super_user_actions=False):
689 691
690 692 from pyramid.threadlocal import get_current_request
691 693 _render = get_current_request().get_partial_renderer(
692 694 'rhodecode:templates/data_table/_dt_elements.mako')
693 695 c = _render.get_call_context()
694 696 h = _render.get_helpers()
695 697
696 698 def quick_menu(repo_group_name):
697 699 return _render('quick_repo_group_menu', repo_group_name)
698 700
699 701 def repo_group_lnk(repo_group_name):
700 702 return _render('repo_group_name', repo_group_name)
701 703
702 704 def last_change(last_change):
703 705 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
704 706 last_change = last_change + datetime.timedelta(seconds=
705 707 (datetime.datetime.now() - datetime.datetime.utcnow()).seconds)
706 708 return _render("last_change", last_change)
707 709
708 710 def desc(desc, personal):
709 711 return _render(
710 712 'repo_group_desc', desc, personal, c.visual.stylify_metatags)
711 713
712 714 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
713 715 return _render(
714 716 'repo_group_actions', repo_group_id, repo_group_name, gr_count)
715 717
716 718 def repo_group_name(repo_group_name, children_groups):
717 719 return _render("repo_group_name", repo_group_name, children_groups)
718 720
719 721 def user_profile(username):
720 722 return _render('user_profile', username)
721 723
722 724 repo_group_data = []
723 725 for group in repo_group_list:
724 726
725 727 row = {
726 728 "menu": quick_menu(group.group_name),
727 729 "name": repo_group_lnk(group.group_name),
728 730 "name_raw": group.group_name,
729 731 "last_change": last_change(group.last_db_change),
730 732 "last_change_raw": datetime_to_time(group.last_db_change),
731 733
732 734 "last_changeset": "",
733 735 "last_changeset_raw": "",
734 736
735 737 "desc": desc(group.description_safe, group.personal),
736 738 "top_level_repos": 0,
737 739 "owner": user_profile(group.user.username)
738 740 }
739 741 if admin:
740 742 repo_count = group.repositories.count()
741 743 children_groups = map(
742 744 h.safe_unicode,
743 745 itertools.chain((g.name for g in group.parents),
744 746 (x.name for x in [group])))
745 747 row.update({
746 748 "action": repo_group_actions(
747 749 group.group_id, group.group_name, repo_count),
748 750 "top_level_repos": repo_count,
749 751 "name": repo_group_name(group.group_name, children_groups),
750 752
751 753 })
752 754 repo_group_data.append(row)
753 755
754 756 return repo_group_data
755 757
756 758 def _get_defaults(self, repo_group_name):
757 759 repo_group = RepoGroup.get_by_group_name(repo_group_name)
758 760
759 761 if repo_group is None:
760 762 return None
761 763
762 764 defaults = repo_group.get_dict()
763 765 defaults['repo_group_name'] = repo_group.name
764 766 defaults['repo_group_description'] = repo_group.group_description
765 767 defaults['repo_group_enable_locking'] = repo_group.enable_locking
766 768
767 769 # we use -1 as this is how in HTML, we mark an empty group
768 770 defaults['repo_group'] = defaults['group_parent_id'] or -1
769 771
770 772 # fill owner
771 773 if repo_group.user:
772 774 defaults.update({'user': repo_group.user.username})
773 775 else:
774 776 replacement_user = User.get_first_super_admin().username
775 777 defaults.update({'user': replacement_user})
776 778
777 779 return defaults
General Comments 0
You need to be logged in to leave comments. Login now