##// END OF EJS Templates
descriptions: fixed rendering problem with certain meta-tags....
bart -
r4223:60c8ef52 stable
parent child Browse files
Show More
@@ -1,1171 +1,1172 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-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 os
22 22 import re
23 23 import shutil
24 24 import time
25 25 import logging
26 26 import traceback
27 27 import datetime
28 28
29 29 from pyramid.threadlocal import get_current_request
30 30 from zope.cachedescriptors.property import Lazy as LazyProperty
31 31
32 32 from rhodecode import events
33 33 from rhodecode.lib.auth import HasUserGroupPermissionAny
34 34 from rhodecode.lib.caching_query import FromCache
35 35 from rhodecode.lib.exceptions import AttachedForksError, AttachedPullRequestsError
36 36 from rhodecode.lib.hooks_base import log_delete_repository
37 37 from rhodecode.lib.user_log_filter import user_log_filter
38 38 from rhodecode.lib.utils import make_db_config
39 39 from rhodecode.lib.utils2 import (
40 40 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
41 get_current_rhodecode_user, safe_int, datetime_to_time,
42 action_logger_generic)
41 get_current_rhodecode_user, safe_int, action_logger_generic)
43 42 from rhodecode.lib.vcs.backends import get_backend
44 43 from rhodecode.model import BaseModel
45 44 from rhodecode.model.db import (
46 45 _hash_key, func, case, joinedload, or_, in_filter_generator,
47 46 Session, Repository, UserRepoToPerm, UserGroupRepoToPerm,
48 47 UserRepoGroupToPerm, UserGroupRepoGroupToPerm, User, Permission,
49 48 Statistics, UserGroup, RepoGroup, RepositoryField, UserLog)
50 49 from rhodecode.model.settings import VcsSettingsModel
51 50
52 51 log = logging.getLogger(__name__)
53 52
54 53
55 54 class RepoModel(BaseModel):
56 55
57 56 cls = Repository
58 57
59 58 def _get_user_group(self, users_group):
60 59 return self._get_instance(UserGroup, users_group,
61 60 callback=UserGroup.get_by_group_name)
62 61
63 62 def _get_repo_group(self, repo_group):
64 63 return self._get_instance(RepoGroup, repo_group,
65 64 callback=RepoGroup.get_by_group_name)
66 65
67 66 def _create_default_perms(self, repository, private):
68 67 # create default permission
69 68 default = 'repository.read'
70 69 def_user = User.get_default_user()
71 70 for p in def_user.user_perms:
72 71 if p.permission.permission_name.startswith('repository.'):
73 72 default = p.permission.permission_name
74 73 break
75 74
76 75 default_perm = 'repository.none' if private else default
77 76
78 77 repo_to_perm = UserRepoToPerm()
79 78 repo_to_perm.permission = Permission.get_by_key(default_perm)
80 79
81 80 repo_to_perm.repository = repository
82 81 repo_to_perm.user_id = def_user.user_id
83 82
84 83 return repo_to_perm
85 84
86 85 @LazyProperty
87 86 def repos_path(self):
88 87 """
89 88 Gets the repositories root path from database
90 89 """
91 90 settings_model = VcsSettingsModel(sa=self.sa)
92 91 return settings_model.get_repos_location()
93 92
94 93 def get(self, repo_id):
95 94 repo = self.sa.query(Repository) \
96 95 .filter(Repository.repo_id == repo_id)
97 96
98 97 return repo.scalar()
99 98
100 99 def get_repo(self, repository):
101 100 return self._get_repo(repository)
102 101
103 102 def get_by_repo_name(self, repo_name, cache=False):
104 103 repo = self.sa.query(Repository) \
105 104 .filter(Repository.repo_name == repo_name)
106 105
107 106 if cache:
108 107 name_key = _hash_key(repo_name)
109 108 repo = repo.options(
110 109 FromCache("sql_cache_short", "get_repo_%s" % name_key))
111 110 return repo.scalar()
112 111
113 112 def _extract_id_from_repo_name(self, repo_name):
114 113 if repo_name.startswith('/'):
115 114 repo_name = repo_name.lstrip('/')
116 115 by_id_match = re.match(r'^_(\d{1,})', repo_name)
117 116 if by_id_match:
118 117 return by_id_match.groups()[0]
119 118
120 119 def get_repo_by_id(self, repo_name):
121 120 """
122 121 Extracts repo_name by id from special urls.
123 122 Example url is _11/repo_name
124 123
125 124 :param repo_name:
126 125 :return: repo object if matched else None
127 126 """
128 127
129 128 try:
130 129 _repo_id = self._extract_id_from_repo_name(repo_name)
131 130 if _repo_id:
132 131 return self.get(_repo_id)
133 132 except Exception:
134 133 log.exception('Failed to extract repo_name from URL')
135 134
136 135 return None
137 136
138 137 def get_repos_for_root(self, root, traverse=False):
139 138 if traverse:
140 139 like_expression = u'{}%'.format(safe_unicode(root))
141 140 repos = Repository.query().filter(
142 141 Repository.repo_name.like(like_expression)).all()
143 142 else:
144 143 if root and not isinstance(root, RepoGroup):
145 144 raise ValueError(
146 145 'Root must be an instance '
147 146 'of RepoGroup, got:{} instead'.format(type(root)))
148 147 repos = Repository.query().filter(Repository.group == root).all()
149 148 return repos
150 149
151 150 def get_url(self, repo, request=None, permalink=False):
152 151 if not request:
153 152 request = get_current_request()
154 153
155 154 if not request:
156 155 return
157 156
158 157 if permalink:
159 158 return request.route_url(
160 159 'repo_summary', repo_name='_{}'.format(safe_str(repo.repo_id)))
161 160 else:
162 161 return request.route_url(
163 162 'repo_summary', repo_name=safe_str(repo.repo_name))
164 163
165 164 def get_commit_url(self, repo, commit_id, request=None, permalink=False):
166 165 if not request:
167 166 request = get_current_request()
168 167
169 168 if not request:
170 169 return
171 170
172 171 if permalink:
173 172 return request.route_url(
174 173 'repo_commit', repo_name=safe_str(repo.repo_id),
175 174 commit_id=commit_id)
176 175
177 176 else:
178 177 return request.route_url(
179 178 'repo_commit', repo_name=safe_str(repo.repo_name),
180 179 commit_id=commit_id)
181 180
182 181 def get_repo_log(self, repo, filter_term):
183 182 repo_log = UserLog.query()\
184 183 .filter(or_(UserLog.repository_id == repo.repo_id,
185 184 UserLog.repository_name == repo.repo_name))\
186 185 .options(joinedload(UserLog.user))\
187 186 .options(joinedload(UserLog.repository))\
188 187 .order_by(UserLog.action_date.desc())
189 188
190 189 repo_log = user_log_filter(repo_log, filter_term)
191 190 return repo_log
192 191
193 192 @classmethod
194 193 def update_commit_cache(cls, repositories=None):
195 194 if not repositories:
196 195 repositories = Repository.getAll()
197 196 for repo in repositories:
198 197 repo.update_commit_cache()
199 198
200 199 def get_repos_as_dict(self, repo_list=None, admin=False,
201 200 super_user_actions=False, short_name=None):
201
202 202 _render = get_current_request().get_partial_renderer(
203 203 'rhodecode:templates/data_table/_dt_elements.mako')
204 204 c = _render.get_call_context()
205 h = _render.get_helpers()
205 206
206 207 def quick_menu(repo_name):
207 208 return _render('quick_menu', repo_name)
208 209
209 210 def repo_lnk(name, rtype, rstate, private, archived, fork_of):
210 211 if short_name is not None:
211 212 short_name_var = short_name
212 213 else:
213 214 short_name_var = not admin
214 215 return _render('repo_name', name, rtype, rstate, private, archived, fork_of,
215 216 short_name=short_name_var, admin=False)
216 217
217 218 def last_change(last_change):
218 219 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
219 220 ts = time.time()
220 221 utc_offset = (datetime.datetime.fromtimestamp(ts)
221 222 - datetime.datetime.utcfromtimestamp(ts)).total_seconds()
222 223 last_change = last_change + datetime.timedelta(seconds=utc_offset)
223 224
224 225 return _render("last_change", last_change)
225 226
226 227 def rss_lnk(repo_name):
227 228 return _render("rss", repo_name)
228 229
229 230 def atom_lnk(repo_name):
230 231 return _render("atom", repo_name)
231 232
232 233 def last_rev(repo_name, cs_cache):
233 234 return _render('revision', repo_name, cs_cache.get('revision'),
234 235 cs_cache.get('raw_id'), cs_cache.get('author'),
235 236 cs_cache.get('message'), cs_cache.get('date'))
236 237
237 238 def desc(desc):
238 239 return _render('repo_desc', desc, c.visual.stylify_metatags)
239 240
240 241 def state(repo_state):
241 242 return _render("repo_state", repo_state)
242 243
243 244 def repo_actions(repo_name):
244 245 return _render('repo_actions', repo_name, super_user_actions)
245 246
246 247 def user_profile(username):
247 248 return _render('user_profile', username)
248 249
249 250 repos_data = []
250 251 for repo in repo_list:
251 252 # NOTE(marcink): because we use only raw column we need to load it like that
252 253 changeset_cache = Repository._load_changeset_cache(
253 254 repo.repo_id, repo._changeset_cache)
254 255
255 256 row = {
256 257 "menu": quick_menu(repo.repo_name),
257 258
258 259 "name": repo_lnk(repo.repo_name, repo.repo_type, repo.repo_state,
259 260 repo.private, repo.archived, repo.fork),
260 261
261 "desc": desc(repo.description),
262 "desc": desc(h.escape(repo.description)),
262 263
263 264 "last_change": last_change(repo.updated_on),
264 265
265 266 "last_changeset": last_rev(repo.repo_name, changeset_cache),
266 267 "last_changeset_raw": changeset_cache.get('revision'),
267 268
268 269 "owner": user_profile(repo.User.username),
269 270
270 271 "state": state(repo.repo_state),
271 272 "rss": rss_lnk(repo.repo_name),
272 273 "atom": atom_lnk(repo.repo_name),
273 274 }
274 275 if admin:
275 276 row.update({
276 277 "action": repo_actions(repo.repo_name),
277 278 })
278 279 repos_data.append(row)
279 280
280 281 return repos_data
281 282
282 283 def get_repos_data_table(
283 284 self, draw, start, limit,
284 285 search_q, order_by, order_dir,
285 286 auth_user, repo_group_id):
286 287 from rhodecode.model.scm import RepoList
287 288
288 289 _perms = ['repository.read', 'repository.write', 'repository.admin']
289 290
290 291 repos = Repository.query() \
291 292 .filter(Repository.group_id == repo_group_id) \
292 293 .all()
293 294 auth_repo_list = RepoList(
294 295 repos, perm_set=_perms,
295 296 extra_kwargs=dict(user=auth_user))
296 297
297 298 allowed_ids = [-1]
298 299 for repo in auth_repo_list:
299 300 allowed_ids.append(repo.repo_id)
300 301
301 302 repos_data_total_count = Repository.query() \
302 303 .filter(Repository.group_id == repo_group_id) \
303 304 .filter(or_(
304 305 # generate multiple IN to fix limitation problems
305 306 *in_filter_generator(Repository.repo_id, allowed_ids))
306 307 ) \
307 308 .count()
308 309
309 310 base_q = Session.query(
310 311 Repository.repo_id,
311 312 Repository.repo_name,
312 313 Repository.description,
313 314 Repository.repo_type,
314 315 Repository.repo_state,
315 316 Repository.private,
316 317 Repository.archived,
317 318 Repository.fork,
318 319 Repository.updated_on,
319 320 Repository._changeset_cache,
320 321 User,
321 322 ) \
322 323 .filter(Repository.group_id == repo_group_id) \
323 324 .filter(or_(
324 325 # generate multiple IN to fix limitation problems
325 326 *in_filter_generator(Repository.repo_id, allowed_ids))
326 327 ) \
327 328 .join(User, User.user_id == Repository.user_id) \
328 329 .group_by(Repository, User)
329 330
330 331 repos_data_total_filtered_count = base_q.count()
331 332
332 333 sort_defined = False
333 334 if order_by == 'repo_name':
334 335 sort_col = func.lower(Repository.repo_name)
335 336 sort_defined = True
336 337 elif order_by == 'user_username':
337 338 sort_col = User.username
338 339 else:
339 340 sort_col = getattr(Repository, order_by, None)
340 341
341 342 if sort_defined or sort_col:
342 343 if order_dir == 'asc':
343 344 sort_col = sort_col.asc()
344 345 else:
345 346 sort_col = sort_col.desc()
346 347
347 348 base_q = base_q.order_by(sort_col)
348 349 base_q = base_q.offset(start).limit(limit)
349 350
350 351 repos_list = base_q.all()
351 352
352 353 repos_data = RepoModel().get_repos_as_dict(
353 354 repo_list=repos_list, admin=False)
354 355
355 356 data = ({
356 357 'draw': draw,
357 358 'data': repos_data,
358 359 'recordsTotal': repos_data_total_count,
359 360 'recordsFiltered': repos_data_total_filtered_count,
360 361 })
361 362 return data
362 363
363 364 def _get_defaults(self, repo_name):
364 365 """
365 366 Gets information about repository, and returns a dict for
366 367 usage in forms
367 368
368 369 :param repo_name:
369 370 """
370 371
371 372 repo_info = Repository.get_by_repo_name(repo_name)
372 373
373 374 if repo_info is None:
374 375 return None
375 376
376 377 defaults = repo_info.get_dict()
377 378 defaults['repo_name'] = repo_info.just_name
378 379
379 380 groups = repo_info.groups_with_parents
380 381 parent_group = groups[-1] if groups else None
381 382
382 383 # we use -1 as this is how in HTML, we mark an empty group
383 384 defaults['repo_group'] = getattr(parent_group, 'group_id', -1)
384 385
385 386 keys_to_process = (
386 387 {'k': 'repo_type', 'strip': False},
387 388 {'k': 'repo_enable_downloads', 'strip': True},
388 389 {'k': 'repo_description', 'strip': True},
389 390 {'k': 'repo_enable_locking', 'strip': True},
390 391 {'k': 'repo_landing_rev', 'strip': True},
391 392 {'k': 'clone_uri', 'strip': False},
392 393 {'k': 'push_uri', 'strip': False},
393 394 {'k': 'repo_private', 'strip': True},
394 395 {'k': 'repo_enable_statistics', 'strip': True}
395 396 )
396 397
397 398 for item in keys_to_process:
398 399 attr = item['k']
399 400 if item['strip']:
400 401 attr = remove_prefix(item['k'], 'repo_')
401 402
402 403 val = defaults[attr]
403 404 if item['k'] == 'repo_landing_rev':
404 405 val = ':'.join(defaults[attr])
405 406 defaults[item['k']] = val
406 407 if item['k'] == 'clone_uri':
407 408 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
408 409 if item['k'] == 'push_uri':
409 410 defaults['push_uri_hidden'] = repo_info.push_uri_hidden
410 411
411 412 # fill owner
412 413 if repo_info.user:
413 414 defaults.update({'user': repo_info.user.username})
414 415 else:
415 416 replacement_user = User.get_first_super_admin().username
416 417 defaults.update({'user': replacement_user})
417 418
418 419 return defaults
419 420
420 421 def update(self, repo, **kwargs):
421 422 try:
422 423 cur_repo = self._get_repo(repo)
423 424 source_repo_name = cur_repo.repo_name
424 425 if 'user' in kwargs:
425 426 cur_repo.user = User.get_by_username(kwargs['user'])
426 427
427 428 if 'repo_group' in kwargs:
428 429 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
429 430 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
430 431
431 432 update_keys = [
432 433 (1, 'repo_description'),
433 434 (1, 'repo_landing_rev'),
434 435 (1, 'repo_private'),
435 436 (1, 'repo_enable_downloads'),
436 437 (1, 'repo_enable_locking'),
437 438 (1, 'repo_enable_statistics'),
438 439 (0, 'clone_uri'),
439 440 (0, 'push_uri'),
440 441 (0, 'fork_id')
441 442 ]
442 443 for strip, k in update_keys:
443 444 if k in kwargs:
444 445 val = kwargs[k]
445 446 if strip:
446 447 k = remove_prefix(k, 'repo_')
447 448
448 449 setattr(cur_repo, k, val)
449 450
450 451 new_name = cur_repo.get_new_name(kwargs['repo_name'])
451 452 cur_repo.repo_name = new_name
452 453
453 454 # if private flag is set, reset default permission to NONE
454 455 if kwargs.get('repo_private'):
455 456 EMPTY_PERM = 'repository.none'
456 457 RepoModel().grant_user_permission(
457 458 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
458 459 )
459 460
460 461 # handle extra fields
461 462 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX), kwargs):
462 463 k = RepositoryField.un_prefix_key(field)
463 464 ex_field = RepositoryField.get_by_key_name(
464 465 key=k, repo=cur_repo)
465 466 if ex_field:
466 467 ex_field.field_value = kwargs[field]
467 468 self.sa.add(ex_field)
468 469
469 470 self.sa.add(cur_repo)
470 471
471 472 if source_repo_name != new_name:
472 473 # rename repository
473 474 self._rename_filesystem_repo(
474 475 old=source_repo_name, new=new_name)
475 476
476 477 return cur_repo
477 478 except Exception:
478 479 log.error(traceback.format_exc())
479 480 raise
480 481
481 482 def _create_repo(self, repo_name, repo_type, description, owner,
482 483 private=False, clone_uri=None, repo_group=None,
483 484 landing_rev='rev:tip', fork_of=None,
484 485 copy_fork_permissions=False, enable_statistics=False,
485 486 enable_locking=False, enable_downloads=False,
486 487 copy_group_permissions=False,
487 488 state=Repository.STATE_PENDING):
488 489 """
489 490 Create repository inside database with PENDING state, this should be
490 491 only executed by create() repo. With exception of importing existing
491 492 repos
492 493 """
493 494 from rhodecode.model.scm import ScmModel
494 495
495 496 owner = self._get_user(owner)
496 497 fork_of = self._get_repo(fork_of)
497 498 repo_group = self._get_repo_group(safe_int(repo_group))
498 499
499 500 try:
500 501 repo_name = safe_unicode(repo_name)
501 502 description = safe_unicode(description)
502 503 # repo name is just a name of repository
503 504 # while repo_name_full is a full qualified name that is combined
504 505 # with name and path of group
505 506 repo_name_full = repo_name
506 507 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
507 508
508 509 new_repo = Repository()
509 510 new_repo.repo_state = state
510 511 new_repo.enable_statistics = False
511 512 new_repo.repo_name = repo_name_full
512 513 new_repo.repo_type = repo_type
513 514 new_repo.user = owner
514 515 new_repo.group = repo_group
515 516 new_repo.description = description or repo_name
516 517 new_repo.private = private
517 518 new_repo.archived = False
518 519 new_repo.clone_uri = clone_uri
519 520 new_repo.landing_rev = landing_rev
520 521
521 522 new_repo.enable_statistics = enable_statistics
522 523 new_repo.enable_locking = enable_locking
523 524 new_repo.enable_downloads = enable_downloads
524 525
525 526 if repo_group:
526 527 new_repo.enable_locking = repo_group.enable_locking
527 528
528 529 if fork_of:
529 530 parent_repo = fork_of
530 531 new_repo.fork = parent_repo
531 532
532 533 events.trigger(events.RepoPreCreateEvent(new_repo))
533 534
534 535 self.sa.add(new_repo)
535 536
536 537 EMPTY_PERM = 'repository.none'
537 538 if fork_of and copy_fork_permissions:
538 539 repo = fork_of
539 540 user_perms = UserRepoToPerm.query() \
540 541 .filter(UserRepoToPerm.repository == repo).all()
541 542 group_perms = UserGroupRepoToPerm.query() \
542 543 .filter(UserGroupRepoToPerm.repository == repo).all()
543 544
544 545 for perm in user_perms:
545 546 UserRepoToPerm.create(
546 547 perm.user, new_repo, perm.permission)
547 548
548 549 for perm in group_perms:
549 550 UserGroupRepoToPerm.create(
550 551 perm.users_group, new_repo, perm.permission)
551 552 # in case we copy permissions and also set this repo to private
552 553 # override the default user permission to make it a private repo
553 554 if private:
554 555 RepoModel(self.sa).grant_user_permission(
555 556 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
556 557
557 558 elif repo_group and copy_group_permissions:
558 559 user_perms = UserRepoGroupToPerm.query() \
559 560 .filter(UserRepoGroupToPerm.group == repo_group).all()
560 561
561 562 group_perms = UserGroupRepoGroupToPerm.query() \
562 563 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
563 564
564 565 for perm in user_perms:
565 566 perm_name = perm.permission.permission_name.replace(
566 567 'group.', 'repository.')
567 568 perm_obj = Permission.get_by_key(perm_name)
568 569 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
569 570
570 571 for perm in group_perms:
571 572 perm_name = perm.permission.permission_name.replace(
572 573 'group.', 'repository.')
573 574 perm_obj = Permission.get_by_key(perm_name)
574 575 UserGroupRepoToPerm.create(perm.users_group, new_repo, perm_obj)
575 576
576 577 if private:
577 578 RepoModel(self.sa).grant_user_permission(
578 579 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
579 580
580 581 else:
581 582 perm_obj = self._create_default_perms(new_repo, private)
582 583 self.sa.add(perm_obj)
583 584
584 585 # now automatically start following this repository as owner
585 586 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id, owner.user_id)
586 587
587 588 # we need to flush here, in order to check if database won't
588 589 # throw any exceptions, create filesystem dirs at the very end
589 590 self.sa.flush()
590 591 events.trigger(events.RepoCreateEvent(new_repo))
591 592 return new_repo
592 593
593 594 except Exception:
594 595 log.error(traceback.format_exc())
595 596 raise
596 597
597 598 def create(self, form_data, cur_user):
598 599 """
599 600 Create repository using celery tasks
600 601
601 602 :param form_data:
602 603 :param cur_user:
603 604 """
604 605 from rhodecode.lib.celerylib import tasks, run_task
605 606 return run_task(tasks.create_repo, form_data, cur_user)
606 607
607 608 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
608 609 perm_deletions=None, check_perms=True,
609 610 cur_user=None):
610 611 if not perm_additions:
611 612 perm_additions = []
612 613 if not perm_updates:
613 614 perm_updates = []
614 615 if not perm_deletions:
615 616 perm_deletions = []
616 617
617 618 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
618 619
619 620 changes = {
620 621 'added': [],
621 622 'updated': [],
622 623 'deleted': [],
623 624 'default_user_changed': None
624 625 }
625 626
626 627 repo = self._get_repo(repo)
627 628
628 629 # update permissions
629 630 for member_id, perm, member_type in perm_updates:
630 631 member_id = int(member_id)
631 632 if member_type == 'user':
632 633 member_name = User.get(member_id).username
633 634 if member_name == User.DEFAULT_USER:
634 635 # NOTE(dan): detect if we changed permissions for default user
635 636 perm_obj = self.sa.query(UserRepoToPerm) \
636 637 .filter(UserRepoToPerm.user_id == member_id) \
637 638 .filter(UserRepoToPerm.repository == repo) \
638 639 .scalar()
639 640 if perm_obj and perm_obj.permission.permission_name != perm:
640 641 changes['default_user_changed'] = True
641 642
642 643 # this updates also current one if found
643 644 self.grant_user_permission(
644 645 repo=repo, user=member_id, perm=perm)
645 646 elif member_type == 'user_group':
646 647 # check if we have permissions to alter this usergroup
647 648 member_name = UserGroup.get(member_id).users_group_name
648 649 if not check_perms or HasUserGroupPermissionAny(
649 650 *req_perms)(member_name, user=cur_user):
650 651 self.grant_user_group_permission(
651 652 repo=repo, group_name=member_id, perm=perm)
652 653 else:
653 654 raise ValueError("member_type must be 'user' or 'user_group' "
654 655 "got {} instead".format(member_type))
655 656 changes['updated'].append({'type': member_type, 'id': member_id,
656 657 'name': member_name, 'new_perm': perm})
657 658
658 659 # set new permissions
659 660 for member_id, perm, member_type in perm_additions:
660 661 member_id = int(member_id)
661 662 if member_type == 'user':
662 663 member_name = User.get(member_id).username
663 664 self.grant_user_permission(
664 665 repo=repo, user=member_id, perm=perm)
665 666 elif member_type == 'user_group':
666 667 # check if we have permissions to alter this usergroup
667 668 member_name = UserGroup.get(member_id).users_group_name
668 669 if not check_perms or HasUserGroupPermissionAny(
669 670 *req_perms)(member_name, user=cur_user):
670 671 self.grant_user_group_permission(
671 672 repo=repo, group_name=member_id, perm=perm)
672 673 else:
673 674 raise ValueError("member_type must be 'user' or 'user_group' "
674 675 "got {} instead".format(member_type))
675 676
676 677 changes['added'].append({'type': member_type, 'id': member_id,
677 678 'name': member_name, 'new_perm': perm})
678 679 # delete permissions
679 680 for member_id, perm, member_type in perm_deletions:
680 681 member_id = int(member_id)
681 682 if member_type == 'user':
682 683 member_name = User.get(member_id).username
683 684 self.revoke_user_permission(repo=repo, user=member_id)
684 685 elif member_type == 'user_group':
685 686 # check if we have permissions to alter this usergroup
686 687 member_name = UserGroup.get(member_id).users_group_name
687 688 if not check_perms or HasUserGroupPermissionAny(
688 689 *req_perms)(member_name, user=cur_user):
689 690 self.revoke_user_group_permission(
690 691 repo=repo, group_name=member_id)
691 692 else:
692 693 raise ValueError("member_type must be 'user' or 'user_group' "
693 694 "got {} instead".format(member_type))
694 695
695 696 changes['deleted'].append({'type': member_type, 'id': member_id,
696 697 'name': member_name, 'new_perm': perm})
697 698 return changes
698 699
699 700 def create_fork(self, form_data, cur_user):
700 701 """
701 702 Simple wrapper into executing celery task for fork creation
702 703
703 704 :param form_data:
704 705 :param cur_user:
705 706 """
706 707 from rhodecode.lib.celerylib import tasks, run_task
707 708 return run_task(tasks.create_repo_fork, form_data, cur_user)
708 709
709 710 def archive(self, repo):
710 711 """
711 712 Archive given repository. Set archive flag.
712 713
713 714 :param repo:
714 715 """
715 716 repo = self._get_repo(repo)
716 717 if repo:
717 718
718 719 try:
719 720 repo.archived = True
720 721 self.sa.add(repo)
721 722 self.sa.commit()
722 723 except Exception:
723 724 log.error(traceback.format_exc())
724 725 raise
725 726
726 727 def delete(self, repo, forks=None, pull_requests=None, fs_remove=True, cur_user=None):
727 728 """
728 729 Delete given repository, forks parameter defines what do do with
729 730 attached forks. Throws AttachedForksError if deleted repo has attached
730 731 forks
731 732
732 733 :param repo:
733 734 :param forks: str 'delete' or 'detach'
734 735 :param pull_requests: str 'delete' or None
735 736 :param fs_remove: remove(archive) repo from filesystem
736 737 """
737 738 if not cur_user:
738 739 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
739 740 repo = self._get_repo(repo)
740 741 if repo:
741 742 if forks == 'detach':
742 743 for r in repo.forks:
743 744 r.fork = None
744 745 self.sa.add(r)
745 746 elif forks == 'delete':
746 747 for r in repo.forks:
747 748 self.delete(r, forks='delete')
748 749 elif [f for f in repo.forks]:
749 750 raise AttachedForksError()
750 751
751 752 # check for pull requests
752 753 pr_sources = repo.pull_requests_source
753 754 pr_targets = repo.pull_requests_target
754 755 if pull_requests != 'delete' and (pr_sources or pr_targets):
755 756 raise AttachedPullRequestsError()
756 757
757 758 old_repo_dict = repo.get_dict()
758 759 events.trigger(events.RepoPreDeleteEvent(repo))
759 760 try:
760 761 self.sa.delete(repo)
761 762 if fs_remove:
762 763 self._delete_filesystem_repo(repo)
763 764 else:
764 765 log.debug('skipping removal from filesystem')
765 766 old_repo_dict.update({
766 767 'deleted_by': cur_user,
767 768 'deleted_on': time.time(),
768 769 })
769 770 log_delete_repository(**old_repo_dict)
770 771 events.trigger(events.RepoDeleteEvent(repo))
771 772 except Exception:
772 773 log.error(traceback.format_exc())
773 774 raise
774 775
775 776 def grant_user_permission(self, repo, user, perm):
776 777 """
777 778 Grant permission for user on given repository, or update existing one
778 779 if found
779 780
780 781 :param repo: Instance of Repository, repository_id, or repository name
781 782 :param user: Instance of User, user_id or username
782 783 :param perm: Instance of Permission, or permission_name
783 784 """
784 785 user = self._get_user(user)
785 786 repo = self._get_repo(repo)
786 787 permission = self._get_perm(perm)
787 788
788 789 # check if we have that permission already
789 790 obj = self.sa.query(UserRepoToPerm) \
790 791 .filter(UserRepoToPerm.user == user) \
791 792 .filter(UserRepoToPerm.repository == repo) \
792 793 .scalar()
793 794 if obj is None:
794 795 # create new !
795 796 obj = UserRepoToPerm()
796 797 obj.repository = repo
797 798 obj.user = user
798 799 obj.permission = permission
799 800 self.sa.add(obj)
800 801 log.debug('Granted perm %s to %s on %s', perm, user, repo)
801 802 action_logger_generic(
802 803 'granted permission: {} to user: {} on repo: {}'.format(
803 804 perm, user, repo), namespace='security.repo')
804 805 return obj
805 806
806 807 def revoke_user_permission(self, repo, user):
807 808 """
808 809 Revoke permission for user on given repository
809 810
810 811 :param repo: Instance of Repository, repository_id, or repository name
811 812 :param user: Instance of User, user_id or username
812 813 """
813 814
814 815 user = self._get_user(user)
815 816 repo = self._get_repo(repo)
816 817
817 818 obj = self.sa.query(UserRepoToPerm) \
818 819 .filter(UserRepoToPerm.repository == repo) \
819 820 .filter(UserRepoToPerm.user == user) \
820 821 .scalar()
821 822 if obj:
822 823 self.sa.delete(obj)
823 824 log.debug('Revoked perm on %s on %s', repo, user)
824 825 action_logger_generic(
825 826 'revoked permission from user: {} on repo: {}'.format(
826 827 user, repo), namespace='security.repo')
827 828
828 829 def grant_user_group_permission(self, repo, group_name, perm):
829 830 """
830 831 Grant permission for user group on given repository, or update
831 832 existing one if found
832 833
833 834 :param repo: Instance of Repository, repository_id, or repository name
834 835 :param group_name: Instance of UserGroup, users_group_id,
835 836 or user group name
836 837 :param perm: Instance of Permission, or permission_name
837 838 """
838 839 repo = self._get_repo(repo)
839 840 group_name = self._get_user_group(group_name)
840 841 permission = self._get_perm(perm)
841 842
842 843 # check if we have that permission already
843 844 obj = self.sa.query(UserGroupRepoToPerm) \
844 845 .filter(UserGroupRepoToPerm.users_group == group_name) \
845 846 .filter(UserGroupRepoToPerm.repository == repo) \
846 847 .scalar()
847 848
848 849 if obj is None:
849 850 # create new
850 851 obj = UserGroupRepoToPerm()
851 852
852 853 obj.repository = repo
853 854 obj.users_group = group_name
854 855 obj.permission = permission
855 856 self.sa.add(obj)
856 857 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
857 858 action_logger_generic(
858 859 'granted permission: {} to usergroup: {} on repo: {}'.format(
859 860 perm, group_name, repo), namespace='security.repo')
860 861
861 862 return obj
862 863
863 864 def revoke_user_group_permission(self, repo, group_name):
864 865 """
865 866 Revoke permission for user group on given repository
866 867
867 868 :param repo: Instance of Repository, repository_id, or repository name
868 869 :param group_name: Instance of UserGroup, users_group_id,
869 870 or user group name
870 871 """
871 872 repo = self._get_repo(repo)
872 873 group_name = self._get_user_group(group_name)
873 874
874 875 obj = self.sa.query(UserGroupRepoToPerm) \
875 876 .filter(UserGroupRepoToPerm.repository == repo) \
876 877 .filter(UserGroupRepoToPerm.users_group == group_name) \
877 878 .scalar()
878 879 if obj:
879 880 self.sa.delete(obj)
880 881 log.debug('Revoked perm to %s on %s', repo, group_name)
881 882 action_logger_generic(
882 883 'revoked permission from usergroup: {} on repo: {}'.format(
883 884 group_name, repo), namespace='security.repo')
884 885
885 886 def delete_stats(self, repo_name):
886 887 """
887 888 removes stats for given repo
888 889
889 890 :param repo_name:
890 891 """
891 892 repo = self._get_repo(repo_name)
892 893 try:
893 894 obj = self.sa.query(Statistics) \
894 895 .filter(Statistics.repository == repo).scalar()
895 896 if obj:
896 897 self.sa.delete(obj)
897 898 except Exception:
898 899 log.error(traceback.format_exc())
899 900 raise
900 901
901 902 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
902 903 field_type='str', field_desc=''):
903 904
904 905 repo = self._get_repo(repo_name)
905 906
906 907 new_field = RepositoryField()
907 908 new_field.repository = repo
908 909 new_field.field_key = field_key
909 910 new_field.field_type = field_type # python type
910 911 new_field.field_value = field_value
911 912 new_field.field_desc = field_desc
912 913 new_field.field_label = field_label
913 914 self.sa.add(new_field)
914 915 return new_field
915 916
916 917 def delete_repo_field(self, repo_name, field_key):
917 918 repo = self._get_repo(repo_name)
918 919 field = RepositoryField.get_by_key_name(field_key, repo)
919 920 if field:
920 921 self.sa.delete(field)
921 922
922 923 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
923 924 clone_uri=None, repo_store_location=None,
924 925 use_global_config=False, install_hooks=True):
925 926 """
926 927 makes repository on filesystem. It's group aware means it'll create
927 928 a repository within a group, and alter the paths accordingly of
928 929 group location
929 930
930 931 :param repo_name:
931 932 :param alias:
932 933 :param parent:
933 934 :param clone_uri:
934 935 :param repo_store_location:
935 936 """
936 937 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
937 938 from rhodecode.model.scm import ScmModel
938 939
939 940 if Repository.NAME_SEP in repo_name:
940 941 raise ValueError(
941 942 'repo_name must not contain groups got `%s`' % repo_name)
942 943
943 944 if isinstance(repo_group, RepoGroup):
944 945 new_parent_path = os.sep.join(repo_group.full_path_splitted)
945 946 else:
946 947 new_parent_path = repo_group or ''
947 948
948 949 if repo_store_location:
949 950 _paths = [repo_store_location]
950 951 else:
951 952 _paths = [self.repos_path, new_parent_path, repo_name]
952 953 # we need to make it str for mercurial
953 954 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
954 955
955 956 # check if this path is not a repository
956 957 if is_valid_repo(repo_path, self.repos_path):
957 958 raise Exception('This path %s is a valid repository' % repo_path)
958 959
959 960 # check if this path is a group
960 961 if is_valid_repo_group(repo_path, self.repos_path):
961 962 raise Exception('This path %s is a valid group' % repo_path)
962 963
963 964 log.info('creating repo %s in %s from url: `%s`',
964 965 repo_name, safe_unicode(repo_path),
965 966 obfuscate_url_pw(clone_uri))
966 967
967 968 backend = get_backend(repo_type)
968 969
969 970 config_repo = None if use_global_config else repo_name
970 971 if config_repo and new_parent_path:
971 972 config_repo = Repository.NAME_SEP.join(
972 973 (new_parent_path, config_repo))
973 974 config = make_db_config(clear_session=False, repo=config_repo)
974 975 config.set('extensions', 'largefiles', '')
975 976
976 977 # patch and reset hooks section of UI config to not run any
977 978 # hooks on creating remote repo
978 979 config.clear_section('hooks')
979 980
980 981 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
981 982 if repo_type == 'git':
982 983 repo = backend(
983 984 repo_path, config=config, create=True, src_url=clone_uri, bare=True,
984 985 with_wire={"cache": False})
985 986 else:
986 987 repo = backend(
987 988 repo_path, config=config, create=True, src_url=clone_uri,
988 989 with_wire={"cache": False})
989 990
990 991 if install_hooks:
991 992 repo.install_hooks()
992 993
993 994 log.debug('Created repo %s with %s backend',
994 995 safe_unicode(repo_name), safe_unicode(repo_type))
995 996 return repo
996 997
997 998 def _rename_filesystem_repo(self, old, new):
998 999 """
999 1000 renames repository on filesystem
1000 1001
1001 1002 :param old: old name
1002 1003 :param new: new name
1003 1004 """
1004 1005 log.info('renaming repo from %s to %s', old, new)
1005 1006
1006 1007 old_path = os.path.join(self.repos_path, old)
1007 1008 new_path = os.path.join(self.repos_path, new)
1008 1009 if os.path.isdir(new_path):
1009 1010 raise Exception(
1010 1011 'Was trying to rename to already existing dir %s' % new_path
1011 1012 )
1012 1013 shutil.move(old_path, new_path)
1013 1014
1014 1015 def _delete_filesystem_repo(self, repo):
1015 1016 """
1016 1017 removes repo from filesystem, the removal is acctually made by
1017 1018 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
1018 1019 repository is no longer valid for rhodecode, can be undeleted later on
1019 1020 by reverting the renames on this repository
1020 1021
1021 1022 :param repo: repo object
1022 1023 """
1023 1024 rm_path = os.path.join(self.repos_path, repo.repo_name)
1024 1025 repo_group = repo.group
1025 1026 log.info("Removing repository %s", rm_path)
1026 1027 # disable hg/git internal that it doesn't get detected as repo
1027 1028 alias = repo.repo_type
1028 1029
1029 1030 config = make_db_config(clear_session=False)
1030 1031 config.set('extensions', 'largefiles', '')
1031 1032 bare = getattr(repo.scm_instance(config=config), 'bare', False)
1032 1033
1033 1034 # skip this for bare git repos
1034 1035 if not bare:
1035 1036 # disable VCS repo
1036 1037 vcs_path = os.path.join(rm_path, '.%s' % alias)
1037 1038 if os.path.exists(vcs_path):
1038 1039 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
1039 1040
1040 1041 _now = datetime.datetime.now()
1041 1042 _ms = str(_now.microsecond).rjust(6, '0')
1042 1043 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
1043 1044 repo.just_name)
1044 1045 if repo_group:
1045 1046 # if repository is in group, prefix the removal path with the group
1046 1047 args = repo_group.full_path_splitted + [_d]
1047 1048 _d = os.path.join(*args)
1048 1049
1049 1050 if os.path.isdir(rm_path):
1050 1051 shutil.move(rm_path, os.path.join(self.repos_path, _d))
1051 1052
1052 1053 # finally cleanup diff-cache if it exists
1053 1054 cached_diffs_dir = repo.cached_diffs_dir
1054 1055 if os.path.isdir(cached_diffs_dir):
1055 1056 shutil.rmtree(cached_diffs_dir)
1056 1057
1057 1058
1058 1059 class ReadmeFinder:
1059 1060 """
1060 1061 Utility which knows how to find a readme for a specific commit.
1061 1062
1062 1063 The main idea is that this is a configurable algorithm. When creating an
1063 1064 instance you can define parameters, currently only the `default_renderer`.
1064 1065 Based on this configuration the method :meth:`search` behaves slightly
1065 1066 different.
1066 1067 """
1067 1068
1068 1069 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
1069 1070 path_re = re.compile(r'^docs?', re.IGNORECASE)
1070 1071
1071 1072 default_priorities = {
1072 1073 None: 0,
1073 1074 '.text': 2,
1074 1075 '.txt': 3,
1075 1076 '.rst': 1,
1076 1077 '.rest': 2,
1077 1078 '.md': 1,
1078 1079 '.mkdn': 2,
1079 1080 '.mdown': 3,
1080 1081 '.markdown': 4,
1081 1082 }
1082 1083
1083 1084 path_priority = {
1084 1085 'doc': 0,
1085 1086 'docs': 1,
1086 1087 }
1087 1088
1088 1089 FALLBACK_PRIORITY = 99
1089 1090
1090 1091 RENDERER_TO_EXTENSION = {
1091 1092 'rst': ['.rst', '.rest'],
1092 1093 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
1093 1094 }
1094 1095
1095 1096 def __init__(self, default_renderer=None):
1096 1097 self._default_renderer = default_renderer
1097 1098 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
1098 1099 default_renderer, [])
1099 1100
1100 1101 def search(self, commit, path='/'):
1101 1102 """
1102 1103 Find a readme in the given `commit`.
1103 1104 """
1104 1105 nodes = commit.get_nodes(path)
1105 1106 matches = self._match_readmes(nodes)
1106 1107 matches = self._sort_according_to_priority(matches)
1107 1108 if matches:
1108 1109 return matches[0].node
1109 1110
1110 1111 paths = self._match_paths(nodes)
1111 1112 paths = self._sort_paths_according_to_priority(paths)
1112 1113 for path in paths:
1113 1114 match = self.search(commit, path=path)
1114 1115 if match:
1115 1116 return match
1116 1117
1117 1118 return None
1118 1119
1119 1120 def _match_readmes(self, nodes):
1120 1121 for node in nodes:
1121 1122 if not node.is_file():
1122 1123 continue
1123 1124 path = node.path.rsplit('/', 1)[-1]
1124 1125 match = self.readme_re.match(path)
1125 1126 if match:
1126 1127 extension = match.group(1)
1127 1128 yield ReadmeMatch(node, match, self._priority(extension))
1128 1129
1129 1130 def _match_paths(self, nodes):
1130 1131 for node in nodes:
1131 1132 if not node.is_dir():
1132 1133 continue
1133 1134 match = self.path_re.match(node.path)
1134 1135 if match:
1135 1136 yield node.path
1136 1137
1137 1138 def _priority(self, extension):
1138 1139 renderer_priority = (
1139 1140 0 if extension in self._renderer_extensions else 1)
1140 1141 extension_priority = self.default_priorities.get(
1141 1142 extension, self.FALLBACK_PRIORITY)
1142 1143 return (renderer_priority, extension_priority)
1143 1144
1144 1145 def _sort_according_to_priority(self, matches):
1145 1146
1146 1147 def priority_and_path(match):
1147 1148 return (match.priority, match.path)
1148 1149
1149 1150 return sorted(matches, key=priority_and_path)
1150 1151
1151 1152 def _sort_paths_according_to_priority(self, paths):
1152 1153
1153 1154 def priority_and_path(path):
1154 1155 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
1155 1156
1156 1157 return sorted(paths, key=priority_and_path)
1157 1158
1158 1159
1159 1160 class ReadmeMatch:
1160 1161
1161 1162 def __init__(self, node, match, priority):
1162 1163 self.node = node
1163 1164 self._match = match
1164 1165 self.priority = priority
1165 1166
1166 1167 @property
1167 1168 def path(self):
1168 1169 return self.node.path
1169 1170
1170 1171 def __repr__(self):
1171 1172 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
@@ -1,886 +1,884 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 time
32 32 import traceback
33 33 import string
34 34
35 35 from zope.cachedescriptors.property import Lazy as LazyProperty
36 36
37 37 from rhodecode import events
38 38 from rhodecode.model import BaseModel
39 39 from rhodecode.model.db import (_hash_key, func, or_, in_filter_generator,
40 40 Session, RepoGroup, UserRepoGroupToPerm, User, Permission, UserGroupRepoGroupToPerm,
41 41 UserGroup, Repository)
42 42 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
43 43 from rhodecode.lib.caching_query import FromCache
44 from rhodecode.lib.utils2 import action_logger_generic, datetime_to_time
44 from rhodecode.lib.utils2 import action_logger_generic
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 48
49 49 class RepoGroupModel(BaseModel):
50 50
51 51 cls = RepoGroup
52 52 PERSONAL_GROUP_DESC = 'personal repo group of user `%(username)s`'
53 53 PERSONAL_GROUP_PATTERN = '${username}' # default
54 54
55 55 def _get_user_group(self, users_group):
56 56 return self._get_instance(UserGroup, users_group,
57 57 callback=UserGroup.get_by_group_name)
58 58
59 59 def _get_repo_group(self, repo_group):
60 60 return self._get_instance(RepoGroup, repo_group,
61 61 callback=RepoGroup.get_by_group_name)
62 62
63 63 @LazyProperty
64 64 def repos_path(self):
65 65 """
66 66 Gets the repositories root path from database
67 67 """
68 68
69 69 settings_model = VcsSettingsModel(sa=self.sa)
70 70 return settings_model.get_repos_location()
71 71
72 72 def get_by_group_name(self, repo_group_name, cache=None):
73 73 repo = self.sa.query(RepoGroup) \
74 74 .filter(RepoGroup.group_name == repo_group_name)
75 75
76 76 if cache:
77 77 name_key = _hash_key(repo_group_name)
78 78 repo = repo.options(
79 79 FromCache("sql_cache_short", "get_repo_group_%s" % name_key))
80 80 return repo.scalar()
81 81
82 82 def get_default_create_personal_repo_group(self):
83 83 value = SettingsModel().get_setting_by_name(
84 84 'create_personal_repo_group')
85 85 return value.app_settings_value if value else None or False
86 86
87 87 def get_personal_group_name_pattern(self):
88 88 value = SettingsModel().get_setting_by_name(
89 89 'personal_repo_group_pattern')
90 90 val = value.app_settings_value if value else None
91 91 group_template = val or self.PERSONAL_GROUP_PATTERN
92 92
93 93 group_template = group_template.lstrip('/')
94 94 return group_template
95 95
96 96 def get_personal_group_name(self, user):
97 97 template = self.get_personal_group_name_pattern()
98 98 return string.Template(template).safe_substitute(
99 99 username=user.username,
100 100 user_id=user.user_id,
101 101 first_name=user.first_name,
102 102 last_name=user.last_name,
103 103 )
104 104
105 105 def create_personal_repo_group(self, user, commit_early=True):
106 106 desc = self.PERSONAL_GROUP_DESC % {'username': user.username}
107 107 personal_repo_group_name = self.get_personal_group_name(user)
108 108
109 109 # create a new one
110 110 RepoGroupModel().create(
111 111 group_name=personal_repo_group_name,
112 112 group_description=desc,
113 113 owner=user.username,
114 114 personal=True,
115 115 commit_early=commit_early)
116 116
117 117 def _create_default_perms(self, new_group):
118 118 # create default permission
119 119 default_perm = 'group.read'
120 120 def_user = User.get_default_user()
121 121 for p in def_user.user_perms:
122 122 if p.permission.permission_name.startswith('group.'):
123 123 default_perm = p.permission.permission_name
124 124 break
125 125
126 126 repo_group_to_perm = UserRepoGroupToPerm()
127 127 repo_group_to_perm.permission = Permission.get_by_key(default_perm)
128 128
129 129 repo_group_to_perm.group = new_group
130 130 repo_group_to_perm.user_id = def_user.user_id
131 131 return repo_group_to_perm
132 132
133 133 def _get_group_name_and_parent(self, group_name_full, repo_in_path=False,
134 134 get_object=False):
135 135 """
136 136 Get's the group name and a parent group name from given group name.
137 137 If repo_in_path is set to truth, we asume the full path also includes
138 138 repo name, in such case we clean the last element.
139 139
140 140 :param group_name_full:
141 141 """
142 142 split_paths = 1
143 143 if repo_in_path:
144 144 split_paths = 2
145 145 _parts = group_name_full.rsplit(RepoGroup.url_sep(), split_paths)
146 146
147 147 if repo_in_path and len(_parts) > 1:
148 148 # such case last element is the repo_name
149 149 _parts.pop(-1)
150 150 group_name_cleaned = _parts[-1] # just the group name
151 151 parent_repo_group_name = None
152 152
153 153 if len(_parts) > 1:
154 154 parent_repo_group_name = _parts[0]
155 155
156 156 parent_group = None
157 157 if parent_repo_group_name:
158 158 parent_group = RepoGroup.get_by_group_name(parent_repo_group_name)
159 159
160 160 if get_object:
161 161 return group_name_cleaned, parent_repo_group_name, parent_group
162 162
163 163 return group_name_cleaned, parent_repo_group_name
164 164
165 165 def check_exist_filesystem(self, group_name, exc_on_failure=True):
166 166 create_path = os.path.join(self.repos_path, group_name)
167 167 log.debug('creating new group in %s', create_path)
168 168
169 169 if os.path.isdir(create_path):
170 170 if exc_on_failure:
171 171 abs_create_path = os.path.abspath(create_path)
172 172 raise Exception('Directory `{}` already exists !'.format(abs_create_path))
173 173 return False
174 174 return True
175 175
176 176 def _create_group(self, group_name):
177 177 """
178 178 makes repository group on filesystem
179 179
180 180 :param repo_name:
181 181 :param parent_id:
182 182 """
183 183
184 184 self.check_exist_filesystem(group_name)
185 185 create_path = os.path.join(self.repos_path, group_name)
186 186 log.debug('creating new group in %s', create_path)
187 187 os.makedirs(create_path, mode=0o755)
188 188 log.debug('created group in %s', create_path)
189 189
190 190 def _rename_group(self, old, new):
191 191 """
192 192 Renames a group on filesystem
193 193
194 194 :param group_name:
195 195 """
196 196
197 197 if old == new:
198 198 log.debug('skipping group rename')
199 199 return
200 200
201 201 log.debug('renaming repository group from %s to %s', old, new)
202 202
203 203 old_path = os.path.join(self.repos_path, old)
204 204 new_path = os.path.join(self.repos_path, new)
205 205
206 206 log.debug('renaming repos paths from %s to %s', old_path, new_path)
207 207
208 208 if os.path.isdir(new_path):
209 209 raise Exception('Was trying to rename to already '
210 210 'existing dir %s' % new_path)
211 211 shutil.move(old_path, new_path)
212 212
213 213 def _delete_filesystem_group(self, group, force_delete=False):
214 214 """
215 215 Deletes a group from a filesystem
216 216
217 217 :param group: instance of group from database
218 218 :param force_delete: use shutil rmtree to remove all objects
219 219 """
220 220 paths = group.full_path.split(RepoGroup.url_sep())
221 221 paths = os.sep.join(paths)
222 222
223 223 rm_path = os.path.join(self.repos_path, paths)
224 224 log.info("Removing group %s", rm_path)
225 225 # delete only if that path really exists
226 226 if os.path.isdir(rm_path):
227 227 if force_delete:
228 228 shutil.rmtree(rm_path)
229 229 else:
230 230 # archive that group`
231 231 _now = datetime.datetime.now()
232 232 _ms = str(_now.microsecond).rjust(6, '0')
233 233 _d = 'rm__%s_GROUP_%s' % (
234 234 _now.strftime('%Y%m%d_%H%M%S_' + _ms), group.name)
235 235 shutil.move(rm_path, os.path.join(self.repos_path, _d))
236 236
237 237 def create(self, group_name, group_description, owner, just_db=False,
238 238 copy_permissions=False, personal=None, commit_early=True):
239 239
240 240 (group_name_cleaned,
241 241 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(group_name)
242 242
243 243 parent_group = None
244 244 if parent_group_name:
245 245 parent_group = self._get_repo_group(parent_group_name)
246 246 if not parent_group:
247 247 # we tried to create a nested group, but the parent is not
248 248 # existing
249 249 raise ValueError(
250 250 'Parent group `%s` given in `%s` group name '
251 251 'is not yet existing.' % (parent_group_name, group_name))
252 252
253 253 # because we are doing a cleanup, we need to check if such directory
254 254 # already exists. If we don't do that we can accidentally delete
255 255 # existing directory via cleanup that can cause data issues, since
256 256 # delete does a folder rename to special syntax later cleanup
257 257 # functions can delete this
258 258 cleanup_group = self.check_exist_filesystem(group_name,
259 259 exc_on_failure=False)
260 260 user = self._get_user(owner)
261 261 if not user:
262 262 raise ValueError('Owner %s not found as rhodecode user', owner)
263 263
264 264 try:
265 265 new_repo_group = RepoGroup()
266 266 new_repo_group.user = user
267 267 new_repo_group.group_description = group_description or group_name
268 268 new_repo_group.parent_group = parent_group
269 269 new_repo_group.group_name = group_name
270 270 new_repo_group.personal = personal
271 271
272 272 self.sa.add(new_repo_group)
273 273
274 274 # create an ADMIN permission for owner except if we're super admin,
275 275 # later owner should go into the owner field of groups
276 276 if not user.is_admin:
277 277 self.grant_user_permission(repo_group=new_repo_group,
278 278 user=owner, perm='group.admin')
279 279
280 280 if parent_group and copy_permissions:
281 281 # copy permissions from parent
282 282 user_perms = UserRepoGroupToPerm.query() \
283 283 .filter(UserRepoGroupToPerm.group == parent_group).all()
284 284
285 285 group_perms = UserGroupRepoGroupToPerm.query() \
286 286 .filter(UserGroupRepoGroupToPerm.group == parent_group).all()
287 287
288 288 for perm in user_perms:
289 289 # don't copy over the permission for user who is creating
290 290 # this group, if he is not super admin he get's admin
291 291 # permission set above
292 292 if perm.user != user or user.is_admin:
293 293 UserRepoGroupToPerm.create(
294 294 perm.user, new_repo_group, perm.permission)
295 295
296 296 for perm in group_perms:
297 297 UserGroupRepoGroupToPerm.create(
298 298 perm.users_group, new_repo_group, perm.permission)
299 299 else:
300 300 perm_obj = self._create_default_perms(new_repo_group)
301 301 self.sa.add(perm_obj)
302 302
303 303 # now commit the changes, earlier so we are sure everything is in
304 304 # the database.
305 305 if commit_early:
306 306 self.sa.commit()
307 307 if not just_db:
308 308 self._create_group(new_repo_group.group_name)
309 309
310 310 # trigger the post hook
311 311 from rhodecode.lib.hooks_base import log_create_repository_group
312 312 repo_group = RepoGroup.get_by_group_name(group_name)
313 313
314 314 # update repo group commit caches initially
315 315 repo_group.update_commit_cache()
316 316
317 317 log_create_repository_group(
318 318 created_by=user.username, **repo_group.get_dict())
319 319
320 320 # Trigger create event.
321 321 events.trigger(events.RepoGroupCreateEvent(repo_group))
322 322
323 323 return new_repo_group
324 324 except Exception:
325 325 self.sa.rollback()
326 326 log.exception('Exception occurred when creating repository group, '
327 327 'doing cleanup...')
328 328 # rollback things manually !
329 329 repo_group = RepoGroup.get_by_group_name(group_name)
330 330 if repo_group:
331 331 RepoGroup.delete(repo_group.group_id)
332 332 self.sa.commit()
333 333 if cleanup_group:
334 334 RepoGroupModel()._delete_filesystem_group(repo_group)
335 335 raise
336 336
337 337 def update_permissions(
338 338 self, repo_group, perm_additions=None, perm_updates=None,
339 339 perm_deletions=None, recursive=None, check_perms=True,
340 340 cur_user=None):
341 341 from rhodecode.model.repo import RepoModel
342 342 from rhodecode.lib.auth import HasUserGroupPermissionAny
343 343
344 344 if not perm_additions:
345 345 perm_additions = []
346 346 if not perm_updates:
347 347 perm_updates = []
348 348 if not perm_deletions:
349 349 perm_deletions = []
350 350
351 351 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
352 352
353 353 changes = {
354 354 'added': [],
355 355 'updated': [],
356 356 'deleted': [],
357 357 'default_user_changed': None
358 358 }
359 359
360 360 def _set_perm_user(obj, user, perm):
361 361 if isinstance(obj, RepoGroup):
362 362 self.grant_user_permission(
363 363 repo_group=obj, user=user, perm=perm)
364 364 elif isinstance(obj, Repository):
365 365 # private repos will not allow to change the default
366 366 # permissions using recursive mode
367 367 if obj.private and user == User.DEFAULT_USER:
368 368 return
369 369
370 370 # we set group permission but we have to switch to repo
371 371 # permission
372 372 perm = perm.replace('group.', 'repository.')
373 373 RepoModel().grant_user_permission(
374 374 repo=obj, user=user, perm=perm)
375 375
376 376 def _set_perm_group(obj, users_group, perm):
377 377 if isinstance(obj, RepoGroup):
378 378 self.grant_user_group_permission(
379 379 repo_group=obj, group_name=users_group, perm=perm)
380 380 elif isinstance(obj, Repository):
381 381 # we set group permission but we have to switch to repo
382 382 # permission
383 383 perm = perm.replace('group.', 'repository.')
384 384 RepoModel().grant_user_group_permission(
385 385 repo=obj, group_name=users_group, perm=perm)
386 386
387 387 def _revoke_perm_user(obj, user):
388 388 if isinstance(obj, RepoGroup):
389 389 self.revoke_user_permission(repo_group=obj, user=user)
390 390 elif isinstance(obj, Repository):
391 391 RepoModel().revoke_user_permission(repo=obj, user=user)
392 392
393 393 def _revoke_perm_group(obj, user_group):
394 394 if isinstance(obj, RepoGroup):
395 395 self.revoke_user_group_permission(
396 396 repo_group=obj, group_name=user_group)
397 397 elif isinstance(obj, Repository):
398 398 RepoModel().revoke_user_group_permission(
399 399 repo=obj, group_name=user_group)
400 400
401 401 # start updates
402 402 log.debug('Now updating permissions for %s in recursive mode:%s',
403 403 repo_group, recursive)
404 404
405 405 # initialize check function, we'll call that multiple times
406 406 has_group_perm = HasUserGroupPermissionAny(*req_perms)
407 407
408 408 for obj in repo_group.recursive_groups_and_repos():
409 409 # iterated obj is an instance of a repos group or repository in
410 410 # that group, recursive option can be: none, repos, groups, all
411 411 if recursive == 'all':
412 412 obj = obj
413 413 elif recursive == 'repos':
414 414 # skip groups, other than this one
415 415 if isinstance(obj, RepoGroup) and not obj == repo_group:
416 416 continue
417 417 elif recursive == 'groups':
418 418 # skip repos
419 419 if isinstance(obj, Repository):
420 420 continue
421 421 else: # recursive == 'none':
422 422 # DEFAULT option - don't apply to iterated objects
423 423 # also we do a break at the end of this loop. if we are not
424 424 # in recursive mode
425 425 obj = repo_group
426 426
427 427 change_obj = obj.get_api_data()
428 428
429 429 # update permissions
430 430 for member_id, perm, member_type in perm_updates:
431 431 member_id = int(member_id)
432 432 if member_type == 'user':
433 433 member_name = User.get(member_id).username
434 434 if isinstance(obj, RepoGroup) and obj == repo_group and member_name == User.DEFAULT_USER:
435 435 # NOTE(dan): detect if we changed permissions for default user
436 436 perm_obj = self.sa.query(UserRepoGroupToPerm) \
437 437 .filter(UserRepoGroupToPerm.user_id == member_id) \
438 438 .filter(UserRepoGroupToPerm.group == repo_group) \
439 439 .scalar()
440 440 if perm_obj and perm_obj.permission.permission_name != perm:
441 441 changes['default_user_changed'] = True
442 442
443 443 # this updates also current one if found
444 444 _set_perm_user(obj, user=member_id, perm=perm)
445 445 elif member_type == 'user_group':
446 446 member_name = UserGroup.get(member_id).users_group_name
447 447 if not check_perms or has_group_perm(member_name,
448 448 user=cur_user):
449 449 _set_perm_group(obj, users_group=member_id, perm=perm)
450 450 else:
451 451 raise ValueError("member_type must be 'user' or 'user_group' "
452 452 "got {} instead".format(member_type))
453 453
454 454 changes['updated'].append(
455 455 {'change_obj': change_obj, 'type': member_type,
456 456 'id': member_id, 'name': member_name, 'new_perm': perm})
457 457
458 458 # set new permissions
459 459 for member_id, perm, member_type in perm_additions:
460 460 member_id = int(member_id)
461 461 if member_type == 'user':
462 462 member_name = User.get(member_id).username
463 463 _set_perm_user(obj, user=member_id, perm=perm)
464 464 elif member_type == 'user_group':
465 465 # check if we have permissions to alter this usergroup
466 466 member_name = UserGroup.get(member_id).users_group_name
467 467 if not check_perms or has_group_perm(member_name,
468 468 user=cur_user):
469 469 _set_perm_group(obj, users_group=member_id, perm=perm)
470 470 else:
471 471 raise ValueError("member_type must be 'user' or 'user_group' "
472 472 "got {} instead".format(member_type))
473 473
474 474 changes['added'].append(
475 475 {'change_obj': change_obj, 'type': member_type,
476 476 'id': member_id, 'name': member_name, 'new_perm': perm})
477 477
478 478 # delete permissions
479 479 for member_id, perm, member_type in perm_deletions:
480 480 member_id = int(member_id)
481 481 if member_type == 'user':
482 482 member_name = User.get(member_id).username
483 483 _revoke_perm_user(obj, user=member_id)
484 484 elif member_type == 'user_group':
485 485 # check if we have permissions to alter this usergroup
486 486 member_name = UserGroup.get(member_id).users_group_name
487 487 if not check_perms or has_group_perm(member_name,
488 488 user=cur_user):
489 489 _revoke_perm_group(obj, user_group=member_id)
490 490 else:
491 491 raise ValueError("member_type must be 'user' or 'user_group' "
492 492 "got {} instead".format(member_type))
493 493
494 494 changes['deleted'].append(
495 495 {'change_obj': change_obj, 'type': member_type,
496 496 'id': member_id, 'name': member_name, 'new_perm': perm})
497 497
498 498 # if it's not recursive call for all,repos,groups
499 499 # break the loop and don't proceed with other changes
500 500 if recursive not in ['all', 'repos', 'groups']:
501 501 break
502 502
503 503 return changes
504 504
505 505 def update(self, repo_group, form_data):
506 506 try:
507 507 repo_group = self._get_repo_group(repo_group)
508 508 old_path = repo_group.full_path
509 509
510 510 # change properties
511 511 if 'group_description' in form_data:
512 512 repo_group.group_description = form_data['group_description']
513 513
514 514 if 'enable_locking' in form_data:
515 515 repo_group.enable_locking = form_data['enable_locking']
516 516
517 517 if 'group_parent_id' in form_data:
518 518 parent_group = (
519 519 self._get_repo_group(form_data['group_parent_id']))
520 520 repo_group.group_parent_id = (
521 521 parent_group.group_id if parent_group else None)
522 522 repo_group.parent_group = parent_group
523 523
524 524 # mikhail: to update the full_path, we have to explicitly
525 525 # update group_name
526 526 group_name = form_data.get('group_name', repo_group.name)
527 527 repo_group.group_name = repo_group.get_new_name(group_name)
528 528
529 529 new_path = repo_group.full_path
530 530
531 531 if 'user' in form_data:
532 532 repo_group.user = User.get_by_username(form_data['user'])
533 533
534 534 self.sa.add(repo_group)
535 535
536 536 # iterate over all members of this groups and do fixes
537 537 # set locking if given
538 538 # if obj is a repoGroup also fix the name of the group according
539 539 # to the parent
540 540 # if obj is a Repo fix it's name
541 541 # this can be potentially heavy operation
542 542 for obj in repo_group.recursive_groups_and_repos():
543 543 # set the value from it's parent
544 544 obj.enable_locking = repo_group.enable_locking
545 545 if isinstance(obj, RepoGroup):
546 546 new_name = obj.get_new_name(obj.name)
547 547 log.debug('Fixing group %s to new name %s',
548 548 obj.group_name, new_name)
549 549 obj.group_name = new_name
550 550
551 551 elif isinstance(obj, Repository):
552 552 # we need to get all repositories from this new group and
553 553 # rename them accordingly to new group path
554 554 new_name = obj.get_new_name(obj.just_name)
555 555 log.debug('Fixing repo %s to new name %s',
556 556 obj.repo_name, new_name)
557 557 obj.repo_name = new_name
558 558
559 559 self.sa.add(obj)
560 560
561 561 self._rename_group(old_path, new_path)
562 562
563 563 # Trigger update event.
564 564 events.trigger(events.RepoGroupUpdateEvent(repo_group))
565 565
566 566 return repo_group
567 567 except Exception:
568 568 log.error(traceback.format_exc())
569 569 raise
570 570
571 571 def delete(self, repo_group, force_delete=False, fs_remove=True):
572 572 repo_group = self._get_repo_group(repo_group)
573 573 if not repo_group:
574 574 return False
575 575 try:
576 576 self.sa.delete(repo_group)
577 577 if fs_remove:
578 578 self._delete_filesystem_group(repo_group, force_delete)
579 579 else:
580 580 log.debug('skipping removal from filesystem')
581 581
582 582 # Trigger delete event.
583 583 events.trigger(events.RepoGroupDeleteEvent(repo_group))
584 584 return True
585 585
586 586 except Exception:
587 587 log.error('Error removing repo_group %s', repo_group)
588 588 raise
589 589
590 590 def grant_user_permission(self, repo_group, user, perm):
591 591 """
592 592 Grant permission for user on given repository group, or update
593 593 existing one if found
594 594
595 595 :param repo_group: Instance of RepoGroup, repositories_group_id,
596 596 or repositories_group name
597 597 :param user: Instance of User, user_id or username
598 598 :param perm: Instance of Permission, or permission_name
599 599 """
600 600
601 601 repo_group = self._get_repo_group(repo_group)
602 602 user = self._get_user(user)
603 603 permission = self._get_perm(perm)
604 604
605 605 # check if we have that permission already
606 606 obj = self.sa.query(UserRepoGroupToPerm)\
607 607 .filter(UserRepoGroupToPerm.user == user)\
608 608 .filter(UserRepoGroupToPerm.group == repo_group)\
609 609 .scalar()
610 610 if obj is None:
611 611 # create new !
612 612 obj = UserRepoGroupToPerm()
613 613 obj.group = repo_group
614 614 obj.user = user
615 615 obj.permission = permission
616 616 self.sa.add(obj)
617 617 log.debug('Granted perm %s to %s on %s', perm, user, repo_group)
618 618 action_logger_generic(
619 619 'granted permission: {} to user: {} on repogroup: {}'.format(
620 620 perm, user, repo_group), namespace='security.repogroup')
621 621 return obj
622 622
623 623 def revoke_user_permission(self, repo_group, user):
624 624 """
625 625 Revoke permission for user on given repository group
626 626
627 627 :param repo_group: Instance of RepoGroup, repositories_group_id,
628 628 or repositories_group name
629 629 :param user: Instance of User, user_id or username
630 630 """
631 631
632 632 repo_group = self._get_repo_group(repo_group)
633 633 user = self._get_user(user)
634 634
635 635 obj = self.sa.query(UserRepoGroupToPerm)\
636 636 .filter(UserRepoGroupToPerm.user == user)\
637 637 .filter(UserRepoGroupToPerm.group == repo_group)\
638 638 .scalar()
639 639 if obj:
640 640 self.sa.delete(obj)
641 641 log.debug('Revoked perm on %s on %s', repo_group, user)
642 642 action_logger_generic(
643 643 'revoked permission from user: {} on repogroup: {}'.format(
644 644 user, repo_group), namespace='security.repogroup')
645 645
646 646 def grant_user_group_permission(self, repo_group, group_name, perm):
647 647 """
648 648 Grant permission for user group on given repository group, or update
649 649 existing one if found
650 650
651 651 :param repo_group: Instance of RepoGroup, repositories_group_id,
652 652 or repositories_group name
653 653 :param group_name: Instance of UserGroup, users_group_id,
654 654 or user group name
655 655 :param perm: Instance of Permission, or permission_name
656 656 """
657 657 repo_group = self._get_repo_group(repo_group)
658 658 group_name = self._get_user_group(group_name)
659 659 permission = self._get_perm(perm)
660 660
661 661 # check if we have that permission already
662 662 obj = self.sa.query(UserGroupRepoGroupToPerm)\
663 663 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
664 664 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
665 665 .scalar()
666 666
667 667 if obj is None:
668 668 # create new
669 669 obj = UserGroupRepoGroupToPerm()
670 670
671 671 obj.group = repo_group
672 672 obj.users_group = group_name
673 673 obj.permission = permission
674 674 self.sa.add(obj)
675 675 log.debug('Granted perm %s to %s on %s', perm, group_name, repo_group)
676 676 action_logger_generic(
677 677 'granted permission: {} to usergroup: {} on repogroup: {}'.format(
678 678 perm, group_name, repo_group), namespace='security.repogroup')
679 679 return obj
680 680
681 681 def revoke_user_group_permission(self, repo_group, group_name):
682 682 """
683 683 Revoke permission for user group on given repository group
684 684
685 685 :param repo_group: Instance of RepoGroup, repositories_group_id,
686 686 or repositories_group name
687 687 :param group_name: Instance of UserGroup, users_group_id,
688 688 or user group name
689 689 """
690 690 repo_group = self._get_repo_group(repo_group)
691 691 group_name = self._get_user_group(group_name)
692 692
693 693 obj = self.sa.query(UserGroupRepoGroupToPerm)\
694 694 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
695 695 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
696 696 .scalar()
697 697 if obj:
698 698 self.sa.delete(obj)
699 699 log.debug('Revoked perm to %s on %s', repo_group, group_name)
700 700 action_logger_generic(
701 701 'revoked permission from usergroup: {} on repogroup: {}'.format(
702 702 group_name, repo_group), namespace='security.repogroup')
703 703
704 704 @classmethod
705 705 def update_commit_cache(cls, repo_groups=None):
706 706 if not repo_groups:
707 707 repo_groups = RepoGroup.getAll()
708 708 for repo_group in repo_groups:
709 709 repo_group.update_commit_cache()
710 710
711
712
713 711 def get_repo_groups_as_dict(self, repo_group_list=None, admin=False,
714 712 super_user_actions=False):
715 713
716 714 from pyramid.threadlocal import get_current_request
717 715 _render = get_current_request().get_partial_renderer(
718 716 'rhodecode:templates/data_table/_dt_elements.mako')
719 717 c = _render.get_call_context()
720 718 h = _render.get_helpers()
721 719
722 720 def quick_menu(repo_group_name):
723 721 return _render('quick_repo_group_menu', repo_group_name)
724 722
725 723 def repo_group_lnk(repo_group_name):
726 724 return _render('repo_group_name', repo_group_name)
727 725
728 726 def last_change(last_change):
729 727 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
730 728 ts = time.time()
731 729 utc_offset = (datetime.datetime.fromtimestamp(ts)
732 730 - datetime.datetime.utcfromtimestamp(ts)).total_seconds()
733 731 last_change = last_change + datetime.timedelta(seconds=utc_offset)
734 732 return _render("last_change", last_change)
735 733
736 734 def desc(desc, personal):
737 735 return _render(
738 736 'repo_group_desc', desc, personal, c.visual.stylify_metatags)
739 737
740 738 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
741 739 return _render(
742 740 'repo_group_actions', repo_group_id, repo_group_name, gr_count)
743 741
744 742 def repo_group_name(repo_group_name, children_groups):
745 743 return _render("repo_group_name", repo_group_name, children_groups)
746 744
747 745 def user_profile(username):
748 746 return _render('user_profile', username)
749 747
750 748 repo_group_data = []
751 749 for group in repo_group_list:
752 750 # NOTE(marcink): because we use only raw column we need to load it like that
753 751 changeset_cache = RepoGroup._load_changeset_cache(
754 752 '', group._changeset_cache)
755 753 last_commit_change = RepoGroup._load_commit_change(changeset_cache)
756 754 row = {
757 755 "menu": quick_menu(group.group_name),
758 756 "name": repo_group_lnk(group.group_name),
759 757 "name_raw": group.group_name,
760 758
761 759 "last_change": last_change(last_commit_change),
762 760
763 761 "last_changeset": "",
764 762 "last_changeset_raw": "",
765 763
766 "desc": desc(group.group_description, group.personal),
764 "desc": desc(h.escape(group.group_description), group.personal),
767 765 "top_level_repos": 0,
768 766 "owner": user_profile(group.User.username)
769 767 }
770 768 if admin:
771 769 repo_count = group.repositories.count()
772 770 children_groups = map(
773 771 h.safe_unicode,
774 772 itertools.chain((g.name for g in group.parents),
775 773 (x.name for x in [group])))
776 774 row.update({
777 775 "action": repo_group_actions(
778 776 group.group_id, group.group_name, repo_count),
779 777 "top_level_repos": repo_count,
780 778 "name": repo_group_name(group.group_name, children_groups),
781 779
782 780 })
783 781 repo_group_data.append(row)
784 782
785 783 return repo_group_data
786 784
787 785 def get_repo_groups_data_table(
788 786 self, draw, start, limit,
789 787 search_q, order_by, order_dir,
790 788 auth_user, repo_group_id):
791 789 from rhodecode.model.scm import RepoGroupList
792 790
793 791 _perms = ['group.read', 'group.write', 'group.admin']
794 792 repo_groups = RepoGroup.query() \
795 793 .filter(RepoGroup.group_parent_id == repo_group_id) \
796 794 .all()
797 795 auth_repo_group_list = RepoGroupList(
798 796 repo_groups, perm_set=_perms,
799 797 extra_kwargs=dict(user=auth_user))
800 798
801 799 allowed_ids = [-1]
802 800 for repo_group in auth_repo_group_list:
803 801 allowed_ids.append(repo_group.group_id)
804 802
805 803 repo_groups_data_total_count = RepoGroup.query() \
806 804 .filter(RepoGroup.group_parent_id == repo_group_id) \
807 805 .filter(or_(
808 806 # generate multiple IN to fix limitation problems
809 807 *in_filter_generator(RepoGroup.group_id, allowed_ids))
810 808 ) \
811 809 .count()
812 810
813 811 base_q = Session.query(
814 812 RepoGroup.group_name,
815 813 RepoGroup.group_name_hash,
816 814 RepoGroup.group_description,
817 815 RepoGroup.group_id,
818 816 RepoGroup.personal,
819 817 RepoGroup.updated_on,
820 818 RepoGroup._changeset_cache,
821 819 User,
822 820 ) \
823 821 .filter(RepoGroup.group_parent_id == repo_group_id) \
824 822 .filter(or_(
825 823 # generate multiple IN to fix limitation problems
826 824 *in_filter_generator(RepoGroup.group_id, allowed_ids))
827 825 ) \
828 826 .join(User, User.user_id == RepoGroup.user_id) \
829 827 .group_by(RepoGroup, User)
830 828
831 829 repo_groups_data_total_filtered_count = base_q.count()
832 830
833 831 sort_defined = False
834 832
835 833 if order_by == 'group_name':
836 834 sort_col = func.lower(RepoGroup.group_name)
837 835 sort_defined = True
838 836 elif order_by == 'user_username':
839 837 sort_col = User.username
840 838 else:
841 839 sort_col = getattr(RepoGroup, order_by, None)
842 840
843 841 if sort_defined or sort_col:
844 842 if order_dir == 'asc':
845 843 sort_col = sort_col.asc()
846 844 else:
847 845 sort_col = sort_col.desc()
848 846
849 847 base_q = base_q.order_by(sort_col)
850 848 base_q = base_q.offset(start).limit(limit)
851 849
852 850 repo_group_list = base_q.all()
853 851
854 852 repo_groups_data = RepoGroupModel().get_repo_groups_as_dict(
855 853 repo_group_list=repo_group_list, admin=False)
856 854
857 855 data = ({
858 856 'draw': draw,
859 857 'data': repo_groups_data,
860 858 'recordsTotal': repo_groups_data_total_count,
861 859 'recordsFiltered': repo_groups_data_total_filtered_count,
862 860 })
863 861 return data
864 862
865 863 def _get_defaults(self, repo_group_name):
866 864 repo_group = RepoGroup.get_by_group_name(repo_group_name)
867 865
868 866 if repo_group is None:
869 867 return None
870 868
871 869 defaults = repo_group.get_dict()
872 870 defaults['repo_group_name'] = repo_group.name
873 871 defaults['repo_group_description'] = repo_group.group_description
874 872 defaults['repo_group_enable_locking'] = repo_group.enable_locking
875 873
876 874 # we use -1 as this is how in HTML, we mark an empty group
877 875 defaults['repo_group'] = defaults['group_parent_id'] or -1
878 876
879 877 # fill owner
880 878 if repo_group.user:
881 879 defaults.update({'user': repo_group.user.username})
882 880 else:
883 881 replacement_user = User.get_first_super_admin().username
884 882 defaults.update({'user': replacement_user})
885 883
886 884 return defaults
General Comments 0
You need to be logged in to leave comments. Login now