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