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