##// END OF EJS Templates
Add flag for permission check in _update_permissions function
marcink -
r3827:ff57547c beta
parent child Browse files
Show More
@@ -1,746 +1,746 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.repo
4 4 ~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Repository model for rhodecode
7 7
8 8 :created_on: Jun 5, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25 from __future__ import with_statement
26 26 import os
27 27 import shutil
28 28 import logging
29 29 import traceback
30 30 from datetime import datetime
31 31
32 32 from rhodecode.lib.vcs.backends import get_backend
33 33 from rhodecode.lib.compat import json
34 34 from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode,\
35 35 remove_prefix, obfuscate_url_pw
36 36 from rhodecode.lib.caching_query import FromCache
37 37 from rhodecode.lib.hooks import log_create_repository, log_delete_repository
38 38
39 39 from rhodecode.model import BaseModel
40 40 from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
41 41 Statistics, UserGroup, UserGroupRepoToPerm, RhodeCodeUi, RepoGroup,\
42 42 RhodeCodeSetting, RepositoryField
43 43 from rhodecode.lib import helpers as h
44 44 from rhodecode.lib.auth import HasRepoPermissionAny, HasUserGroupPermissionAny
45 45 from rhodecode.lib.exceptions import AttachedForksError
46 46 from rhodecode.model.scm import UserGroupList
47 47
48 48 log = logging.getLogger(__name__)
49 49
50 50
51 51 class RepoModel(BaseModel):
52 52
53 53 cls = Repository
54 54 URL_SEPARATOR = Repository.url_sep()
55 55
56 56 def _get_user_group(self, users_group):
57 57 return self._get_instance(UserGroup, users_group,
58 58 callback=UserGroup.get_by_group_name)
59 59
60 60 def _get_repo_group(self, repos_group):
61 61 return self._get_instance(RepoGroup, repos_group,
62 62 callback=RepoGroup.get_by_group_name)
63 63
64 64 def _create_default_perms(self, repository, private):
65 65 # create default permission
66 66 default = 'repository.read'
67 67 def_user = User.get_default_user()
68 68 for p in def_user.user_perms:
69 69 if p.permission.permission_name.startswith('repository.'):
70 70 default = p.permission.permission_name
71 71 break
72 72
73 73 default_perm = 'repository.none' if private else default
74 74
75 75 repo_to_perm = UserRepoToPerm()
76 76 repo_to_perm.permission = Permission.get_by_key(default_perm)
77 77
78 78 repo_to_perm.repository = repository
79 79 repo_to_perm.user_id = def_user.user_id
80 80
81 81 return repo_to_perm
82 82
83 83 @LazyProperty
84 84 def repos_path(self):
85 85 """
86 86 Get's the repositories root path from database
87 87 """
88 88
89 89 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
90 90 return q.ui_value
91 91
92 92 def get(self, repo_id, cache=False):
93 93 repo = self.sa.query(Repository)\
94 94 .filter(Repository.repo_id == repo_id)
95 95
96 96 if cache:
97 97 repo = repo.options(FromCache("sql_cache_short",
98 98 "get_repo_%s" % repo_id))
99 99 return repo.scalar()
100 100
101 101 def get_repo(self, repository):
102 102 return self._get_repo(repository)
103 103
104 104 def get_by_repo_name(self, repo_name, cache=False):
105 105 repo = self.sa.query(Repository)\
106 106 .filter(Repository.repo_name == repo_name)
107 107
108 108 if cache:
109 109 repo = repo.options(FromCache("sql_cache_short",
110 110 "get_repo_%s" % repo_name))
111 111 return repo.scalar()
112 112
113 113 def get_all_user_repos(self, user):
114 114 """
115 115 Get's all repositories that user have at least read access
116 116
117 117 :param user:
118 118 :type user:
119 119 """
120 120 from rhodecode.lib.auth import AuthUser
121 121 user = self._get_user(user)
122 122 repos = AuthUser(user_id=user.user_id).permissions['repositories']
123 123 access_check = lambda r: r[1] in ['repository.read',
124 124 'repository.write',
125 125 'repository.admin']
126 126 repos = [x[0] for x in filter(access_check, repos.items())]
127 127 return Repository.query().filter(Repository.repo_name.in_(repos))
128 128
129 129 def get_users_js(self):
130 130 users = self.sa.query(User).filter(User.active == True).all()
131 131 return json.dumps([
132 132 {
133 133 'id': u.user_id,
134 134 'fname': u.name,
135 135 'lname': u.lastname,
136 136 'nname': u.username,
137 137 'gravatar_lnk': h.gravatar_url(u.email, 14)
138 138 } for u in users]
139 139 )
140 140
141 141 def get_users_groups_js(self):
142 142 users_groups = self.sa.query(UserGroup)\
143 143 .filter(UserGroup.users_group_active == True).all()
144 144 users_groups = UserGroupList(users_groups, perm_set=['usergroup.read',
145 145 'usergroup.write',
146 146 'usergroup.admin'])
147 147 return json.dumps([
148 148 {
149 149 'id': gr.users_group_id,
150 150 'grname': gr.users_group_name,
151 151 'grmembers': len(gr.members),
152 152 } for gr in users_groups]
153 153 )
154 154
155 155 @classmethod
156 156 def _render_datatable(cls, tmpl, *args, **kwargs):
157 157 import rhodecode
158 158 from pylons import tmpl_context as c
159 159 from pylons.i18n.translation import _
160 160
161 161 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
162 162 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
163 163
164 164 tmpl = template.get_def(tmpl)
165 165 kwargs.update(dict(_=_, h=h, c=c))
166 166 return tmpl.render(*args, **kwargs)
167 167
168 168 @classmethod
169 169 def update_repoinfo(cls, repositories=None):
170 170 if not repositories:
171 171 repositories = Repository.getAll()
172 172 for repo in repositories:
173 173 repo.update_changeset_cache()
174 174
175 175 def get_repos_as_dict(self, repos_list=None, admin=False, perm_check=True,
176 176 super_user_actions=False):
177 177 _render = self._render_datatable
178 from pylons import tmpl_context as c
178 179
179 180 def quick_menu(repo_name):
180 181 return _render('quick_menu', repo_name)
181 182
182 183 def repo_lnk(name, rtype, private, fork_of):
183 184 return _render('repo_name', name, rtype, private, fork_of,
184 185 short_name=not admin, admin=False)
185 186
186 187 def last_change(last_change):
187 188 return _render("last_change", last_change)
188 189
189 190 def rss_lnk(repo_name):
190 191 return _render("rss", repo_name)
191 192
192 193 def atom_lnk(repo_name):
193 194 return _render("atom", repo_name)
194 195
195 196 def last_rev(repo_name, cs_cache):
196 197 return _render('revision', repo_name, cs_cache.get('revision'),
197 198 cs_cache.get('raw_id'), cs_cache.get('author'),
198 199 cs_cache.get('message'))
199 200
200 201 def desc(desc):
201 from pylons import tmpl_context as c
202 202 if c.visual.stylify_metatags:
203 203 return h.urlify_text(h.desc_stylize(h.truncate(desc, 60)))
204 204 else:
205 205 return h.urlify_text(h.truncate(desc, 60))
206 206
207 207 def repo_actions(repo_name):
208 208 return _render('repo_actions', repo_name, super_user_actions)
209 209
210 210 def owner_actions(user_id, username):
211 211 return _render('user_name', user_id, username)
212 212
213 213 repos_data = []
214 214 for repo in repos_list:
215 215 if perm_check:
216 216 # check permission at this level
217 217 if not HasRepoPermissionAny(
218 218 'repository.read', 'repository.write', 'repository.admin'
219 219 )(repo.repo_name, 'get_repos_as_dict check'):
220 220 continue
221 221 cs_cache = repo.changeset_cache
222 222 row = {
223 223 "menu": quick_menu(repo.repo_name),
224 224 "raw_name": repo.repo_name.lower(),
225 225 "name": repo_lnk(repo.repo_name, repo.repo_type,
226 226 repo.private, repo.fork),
227 227 "last_change": last_change(repo.last_db_change),
228 228 "last_changeset": last_rev(repo.repo_name, cs_cache),
229 229 "raw_tip": cs_cache.get('revision'),
230 230 "desc": desc(repo.description),
231 231 "owner": h.person(repo.user.username),
232 232 "rss": rss_lnk(repo.repo_name),
233 233 "atom": atom_lnk(repo.repo_name),
234 234
235 235 }
236 236 if admin:
237 237 row.update({
238 238 "action": repo_actions(repo.repo_name),
239 239 "owner": owner_actions(repo.user.user_id,
240 240 h.person(repo.user.username))
241 241 })
242 242 repos_data.append(row)
243 243
244 244 return {
245 245 "totalRecords": len(repos_list),
246 246 "startIndex": 0,
247 247 "sort": "name",
248 248 "dir": "asc",
249 249 "records": repos_data
250 250 }
251 251
252 252 def _get_defaults(self, repo_name):
253 253 """
254 254 Get's information about repository, and returns a dict for
255 255 usage in forms
256 256
257 257 :param repo_name:
258 258 """
259 259
260 260 repo_info = Repository.get_by_repo_name(repo_name)
261 261
262 262 if repo_info is None:
263 263 return None
264 264
265 265 defaults = repo_info.get_dict()
266 266 group, repo_name, repo_name_full = repo_info.groups_and_repo
267 267 defaults['repo_name'] = repo_name
268 268 defaults['repo_group'] = getattr(group[-1] if group else None,
269 269 'group_id', None)
270 270
271 271 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
272 272 (1, 'repo_description'), (1, 'repo_enable_locking'),
273 273 (1, 'repo_landing_rev'), (0, 'clone_uri'),
274 274 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
275 275 attr = k
276 276 if strip:
277 277 attr = remove_prefix(k, 'repo_')
278 278
279 279 defaults[k] = defaults[attr]
280 280
281 281 # fill owner
282 282 if repo_info.user:
283 283 defaults.update({'user': repo_info.user.username})
284 284 else:
285 285 replacement_user = User.query().filter(User.admin ==
286 286 True).first().username
287 287 defaults.update({'user': replacement_user})
288 288
289 289 # fill repository users
290 290 for p in repo_info.repo_to_perm:
291 291 defaults.update({'u_perm_%s' % p.user.username:
292 292 p.permission.permission_name})
293 293
294 294 # fill repository groups
295 295 for p in repo_info.users_group_to_perm:
296 296 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
297 297 p.permission.permission_name})
298 298
299 299 return defaults
300 300
301 301 def update(self, org_repo_name, **kwargs):
302 302 try:
303 303 cur_repo = self.get_by_repo_name(org_repo_name, cache=False)
304 304
305 305 if 'user' in kwargs:
306 306 cur_repo.user = User.get_by_username(kwargs['user'])
307 307
308 308 if 'repo_group' in kwargs:
309 309 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
310 310
311 311 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
312 312 (1, 'repo_description'), (1, 'repo_enable_locking'),
313 313 (1, 'repo_landing_rev'), (0, 'clone_uri'),
314 314 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
315 315 if k in kwargs:
316 316 val = kwargs[k]
317 317 if strip:
318 318 k = remove_prefix(k, 'repo_')
319 319 setattr(cur_repo, k, val)
320 320
321 321 new_name = cur_repo.get_new_name(kwargs['repo_name'])
322 322 cur_repo.repo_name = new_name
323 323 #if private flag is set, reset default permission to NONE
324 324
325 325 if kwargs.get('repo_private'):
326 326 EMPTY_PERM = 'repository.none'
327 327 RepoModel().grant_user_permission(
328 328 repo=cur_repo, user='default', perm=EMPTY_PERM
329 329 )
330 330 #handle extra fields
331 331 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX), kwargs):
332 332 k = RepositoryField.un_prefix_key(field)
333 333 ex_field = RepositoryField.get_by_key_name(key=k, repo=cur_repo)
334 334 if ex_field:
335 335 ex_field.field_value = kwargs[field]
336 336 self.sa.add(ex_field)
337 337 self.sa.add(cur_repo)
338 338
339 339 if org_repo_name != new_name:
340 340 # rename repository
341 341 self.__rename_repo(old=org_repo_name, new=new_name)
342 342
343 343 return cur_repo
344 344 except Exception:
345 345 log.error(traceback.format_exc())
346 346 raise
347 347
348 348 def create_repo(self, repo_name, repo_type, description, owner,
349 349 private=False, clone_uri=None, repos_group=None,
350 350 landing_rev='tip', just_db=False, fork_of=None,
351 351 copy_fork_permissions=False, enable_statistics=False,
352 352 enable_locking=False, enable_downloads=False):
353 353 """
354 354 Create repository
355 355
356 356 """
357 357 from rhodecode.model.scm import ScmModel
358 358
359 359 owner = self._get_user(owner)
360 360 fork_of = self._get_repo(fork_of)
361 361 repos_group = self._get_repo_group(repos_group)
362 362 try:
363 363
364 364 # repo name is just a name of repository
365 365 # while repo_name_full is a full qualified name that is combined
366 366 # with name and path of group
367 367 repo_name_full = repo_name
368 368 repo_name = repo_name.split(self.URL_SEPARATOR)[-1]
369 369
370 370 new_repo = Repository()
371 371 new_repo.enable_statistics = False
372 372 new_repo.repo_name = repo_name_full
373 373 new_repo.repo_type = repo_type
374 374 new_repo.user = owner
375 375 new_repo.group = repos_group
376 376 new_repo.description = description or repo_name
377 377 new_repo.private = private
378 378 new_repo.clone_uri = clone_uri
379 379 new_repo.landing_rev = landing_rev
380 380
381 381 new_repo.enable_statistics = enable_statistics
382 382 new_repo.enable_locking = enable_locking
383 383 new_repo.enable_downloads = enable_downloads
384 384
385 385 if repos_group:
386 386 new_repo.enable_locking = repos_group.enable_locking
387 387
388 388 if fork_of:
389 389 parent_repo = fork_of
390 390 new_repo.fork = parent_repo
391 391
392 392 self.sa.add(new_repo)
393 393
394 394 if fork_of:
395 395 if copy_fork_permissions:
396 396 repo = fork_of
397 397 user_perms = UserRepoToPerm.query()\
398 398 .filter(UserRepoToPerm.repository == repo).all()
399 399 group_perms = UserGroupRepoToPerm.query()\
400 400 .filter(UserGroupRepoToPerm.repository == repo).all()
401 401
402 402 for perm in user_perms:
403 403 UserRepoToPerm.create(perm.user, new_repo,
404 404 perm.permission)
405 405
406 406 for perm in group_perms:
407 407 UserGroupRepoToPerm.create(perm.users_group, new_repo,
408 408 perm.permission)
409 409 else:
410 410 perm_obj = self._create_default_perms(new_repo, private)
411 411 self.sa.add(perm_obj)
412 412 else:
413 413 perm_obj = self._create_default_perms(new_repo, private)
414 414 self.sa.add(perm_obj)
415 415
416 416 if not just_db:
417 417 self.__create_repo(repo_name, repo_type,
418 418 repos_group,
419 419 clone_uri)
420 420 log_create_repository(new_repo.get_dict(),
421 421 created_by=owner.username)
422 422
423 423 # now automatically start following this repository as owner
424 424 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
425 425 owner.user_id)
426 426 return new_repo
427 427 except Exception:
428 428 log.error(traceback.format_exc())
429 429 raise
430 430
431 431 def create(self, form_data, cur_user, just_db=False, fork=None):
432 432 """
433 433 Backward compatibility function, just a wrapper on top of create_repo
434 434
435 435 :param form_data:
436 436 :param cur_user:
437 437 :param just_db:
438 438 :param fork:
439 439 """
440 440 owner = cur_user
441 441 repo_name = form_data['repo_name_full']
442 442 repo_type = form_data['repo_type']
443 443 description = form_data['repo_description']
444 444 private = form_data['repo_private']
445 445 clone_uri = form_data.get('clone_uri')
446 446 repos_group = form_data['repo_group']
447 447 landing_rev = form_data['repo_landing_rev']
448 448 copy_fork_permissions = form_data.get('copy_permissions')
449 449 fork_of = form_data.get('fork_parent_id')
450 450
451 451 ## repo creation defaults, private and repo_type are filled in form
452 452 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
453 453 enable_statistics = defs.get('repo_enable_statistics')
454 454 enable_locking = defs.get('repo_enable_locking')
455 455 enable_downloads = defs.get('repo_enable_downloads')
456 456
457 457 return self.create_repo(
458 458 repo_name, repo_type, description, owner, private, clone_uri,
459 459 repos_group, landing_rev, just_db, fork_of, copy_fork_permissions,
460 460 enable_statistics, enable_locking, enable_downloads
461 461 )
462 462
463 def _update_permissions(self, repo, perms_new=None,
464 perms_updates=None):
463 def _update_permissions(self, repo, perms_new=None, perms_updates=None,
464 check_perms=True):
465 465 if not perms_new:
466 466 perms_new = []
467 467 if not perms_updates:
468 468 perms_updates = []
469 469
470 470 # update permissions
471 471 for member, perm, member_type in perms_updates:
472 472 if member_type == 'user':
473 473 # this updates existing one
474 474 self.grant_user_permission(
475 475 repo=repo, user=member, perm=perm
476 476 )
477 477 else:
478 478 #check if we have permissions to alter this usergroup
479 if HasUserGroupPermissionAny('usergroup.read', 'usergroup.write',
480 'usergroup.admin')(member):
479 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
480 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member):
481 481 self.grant_users_group_permission(
482 482 repo=repo, group_name=member, perm=perm
483 483 )
484 484 # set new permissions
485 485 for member, perm, member_type in perms_new:
486 486 if member_type == 'user':
487 487 self.grant_user_permission(
488 488 repo=repo, user=member, perm=perm
489 489 )
490 490 else:
491 491 #check if we have permissions to alter this usergroup
492 if HasUserGroupPermissionAny('usergroup.read', 'usergroup.write',
493 'usergroup.admin')(member):
492 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
493 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member):
494 494 self.grant_users_group_permission(
495 495 repo=repo, group_name=member, perm=perm
496 496 )
497 497
498 498 def create_fork(self, form_data, cur_user):
499 499 """
500 500 Simple wrapper into executing celery task for fork creation
501 501
502 502 :param form_data:
503 503 :param cur_user:
504 504 """
505 505 from rhodecode.lib.celerylib import tasks, run_task
506 506 run_task(tasks.create_repo_fork, form_data, cur_user)
507 507
508 508 def delete(self, repo, forks=None, fs_remove=True):
509 509 """
510 510 Delete given repository, forks parameter defines what do do with
511 511 attached forks. Throws AttachedForksError if deleted repo has attached
512 512 forks
513 513
514 514 :param repo:
515 515 :param forks: str 'delete' or 'detach'
516 516 :param fs_remove: remove(archive) repo from filesystem
517 517 """
518 518 repo = self._get_repo(repo)
519 519 if repo:
520 520 if forks == 'detach':
521 521 for r in repo.forks:
522 522 r.fork = None
523 523 self.sa.add(r)
524 524 elif forks == 'delete':
525 525 for r in repo.forks:
526 526 self.delete(r, forks='delete')
527 527 elif [f for f in repo.forks]:
528 528 raise AttachedForksError()
529 529
530 530 old_repo_dict = repo.get_dict()
531 531 owner = repo.user
532 532 try:
533 533 self.sa.delete(repo)
534 534 if fs_remove:
535 535 self.__delete_repo(repo)
536 536 else:
537 537 log.debug('skipping removal from filesystem')
538 538 log_delete_repository(old_repo_dict,
539 539 deleted_by=owner.username)
540 540 except Exception:
541 541 log.error(traceback.format_exc())
542 542 raise
543 543
544 544 def grant_user_permission(self, repo, user, perm):
545 545 """
546 546 Grant permission for user on given repository, or update existing one
547 547 if found
548 548
549 549 :param repo: Instance of Repository, repository_id, or repository name
550 550 :param user: Instance of User, user_id or username
551 551 :param perm: Instance of Permission, or permission_name
552 552 """
553 553 user = self._get_user(user)
554 554 repo = self._get_repo(repo)
555 555 permission = self._get_perm(perm)
556 556
557 557 # check if we have that permission already
558 558 obj = self.sa.query(UserRepoToPerm)\
559 559 .filter(UserRepoToPerm.user == user)\
560 560 .filter(UserRepoToPerm.repository == repo)\
561 561 .scalar()
562 562 if obj is None:
563 563 # create new !
564 564 obj = UserRepoToPerm()
565 565 obj.repository = repo
566 566 obj.user = user
567 567 obj.permission = permission
568 568 self.sa.add(obj)
569 569 log.debug('Granted perm %s to %s on %s' % (perm, user, repo))
570 570
571 571 def revoke_user_permission(self, repo, user):
572 572 """
573 573 Revoke permission for user on given repository
574 574
575 575 :param repo: Instance of Repository, repository_id, or repository name
576 576 :param user: Instance of User, user_id or username
577 577 """
578 578
579 579 user = self._get_user(user)
580 580 repo = self._get_repo(repo)
581 581
582 582 obj = self.sa.query(UserRepoToPerm)\
583 583 .filter(UserRepoToPerm.repository == repo)\
584 584 .filter(UserRepoToPerm.user == user)\
585 585 .scalar()
586 586 if obj:
587 587 self.sa.delete(obj)
588 588 log.debug('Revoked perm on %s on %s' % (repo, user))
589 589
590 590 def grant_users_group_permission(self, repo, group_name, perm):
591 591 """
592 592 Grant permission for user group on given repository, or update
593 593 existing one if found
594 594
595 595 :param repo: Instance of Repository, repository_id, or repository name
596 596 :param group_name: Instance of UserGroup, users_group_id,
597 597 or user group name
598 598 :param perm: Instance of Permission, or permission_name
599 599 """
600 600 repo = self._get_repo(repo)
601 601 group_name = self._get_user_group(group_name)
602 602 permission = self._get_perm(perm)
603 603
604 604 # check if we have that permission already
605 605 obj = self.sa.query(UserGroupRepoToPerm)\
606 606 .filter(UserGroupRepoToPerm.users_group == group_name)\
607 607 .filter(UserGroupRepoToPerm.repository == repo)\
608 608 .scalar()
609 609
610 610 if obj is None:
611 611 # create new
612 612 obj = UserGroupRepoToPerm()
613 613
614 614 obj.repository = repo
615 615 obj.users_group = group_name
616 616 obj.permission = permission
617 617 self.sa.add(obj)
618 618 log.debug('Granted perm %s to %s on %s' % (perm, group_name, repo))
619 619
620 620 def revoke_users_group_permission(self, repo, group_name):
621 621 """
622 622 Revoke permission for user group on given repository
623 623
624 624 :param repo: Instance of Repository, repository_id, or repository name
625 625 :param group_name: Instance of UserGroup, users_group_id,
626 626 or user group name
627 627 """
628 628 repo = self._get_repo(repo)
629 629 group_name = self._get_user_group(group_name)
630 630
631 631 obj = self.sa.query(UserGroupRepoToPerm)\
632 632 .filter(UserGroupRepoToPerm.repository == repo)\
633 633 .filter(UserGroupRepoToPerm.users_group == group_name)\
634 634 .scalar()
635 635 if obj:
636 636 self.sa.delete(obj)
637 637 log.debug('Revoked perm to %s on %s' % (repo, group_name))
638 638
639 639 def delete_stats(self, repo_name):
640 640 """
641 641 removes stats for given repo
642 642
643 643 :param repo_name:
644 644 """
645 645 repo = self._get_repo(repo_name)
646 646 try:
647 647 obj = self.sa.query(Statistics)\
648 648 .filter(Statistics.repository == repo).scalar()
649 649 if obj:
650 650 self.sa.delete(obj)
651 651 except Exception:
652 652 log.error(traceback.format_exc())
653 653 raise
654 654
655 655 def __create_repo(self, repo_name, alias, parent, clone_uri=False):
656 656 """
657 657 makes repository on filesystem. It's group aware means it'll create
658 658 a repository within a group, and alter the paths accordingly of
659 659 group location
660 660
661 661 :param repo_name:
662 662 :param alias:
663 663 :param parent_id:
664 664 :param clone_uri:
665 665 """
666 666 from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
667 667 from rhodecode.model.scm import ScmModel
668 668
669 669 if parent:
670 670 new_parent_path = os.sep.join(parent.full_path_splitted)
671 671 else:
672 672 new_parent_path = ''
673 673
674 674 # we need to make it str for mercurial
675 675 repo_path = os.path.join(*map(lambda x: safe_str(x),
676 676 [self.repos_path, new_parent_path, repo_name]))
677 677
678 678 # check if this path is not a repository
679 679 if is_valid_repo(repo_path, self.repos_path):
680 680 raise Exception('This path %s is a valid repository' % repo_path)
681 681
682 682 # check if this path is a group
683 683 if is_valid_repos_group(repo_path, self.repos_path):
684 684 raise Exception('This path %s is a valid group' % repo_path)
685 685
686 686 log.info('creating repo %s in %s @ %s' % (
687 687 repo_name, safe_unicode(repo_path),
688 688 obfuscate_url_pw(clone_uri)
689 689 )
690 690 )
691 691 backend = get_backend(alias)
692 692 if alias == 'hg':
693 693 backend(repo_path, create=True, src_url=clone_uri)
694 694 elif alias == 'git':
695 695 r = backend(repo_path, create=True, src_url=clone_uri, bare=True)
696 696 # add rhodecode hook into this repo
697 697 ScmModel().install_git_hook(repo=r)
698 698 else:
699 699 raise Exception('Undefined alias %s' % alias)
700 700
701 701 def __rename_repo(self, old, new):
702 702 """
703 703 renames repository on filesystem
704 704
705 705 :param old: old name
706 706 :param new: new name
707 707 """
708 708 log.info('renaming repo from %s to %s' % (old, new))
709 709
710 710 old_path = os.path.join(self.repos_path, old)
711 711 new_path = os.path.join(self.repos_path, new)
712 712 if os.path.isdir(new_path):
713 713 raise Exception(
714 714 'Was trying to rename to already existing dir %s' % new_path
715 715 )
716 716 shutil.move(old_path, new_path)
717 717
718 718 def __delete_repo(self, repo):
719 719 """
720 720 removes repo from filesystem, the removal is acctually made by
721 721 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
722 722 repository is no longer valid for rhodecode, can be undeleted later on
723 723 by reverting the renames on this repository
724 724
725 725 :param repo: repo object
726 726 """
727 727 rm_path = os.path.join(self.repos_path, repo.repo_name)
728 728 log.info("Removing %s" % (rm_path))
729 729 # disable hg/git internal that it doesn't get detected as repo
730 730 alias = repo.repo_type
731 731
732 732 bare = getattr(repo.scm_instance, 'bare', False)
733 733
734 734 if not bare:
735 735 # skip this for bare git repos
736 736 shutil.move(os.path.join(rm_path, '.%s' % alias),
737 737 os.path.join(rm_path, 'rm__.%s' % alias))
738 738 # disable repo
739 739 _now = datetime.now()
740 740 _ms = str(_now.microsecond).rjust(6, '0')
741 741 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
742 742 repo.just_name)
743 743 if repo.group:
744 744 args = repo.group.full_path_splitted + [_d]
745 745 _d = os.path.join(*args)
746 746 shutil.move(rm_path, os.path.join(self.repos_path, _d))
@@ -1,436 +1,438 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.user_group
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 repo group model for RhodeCode
7 7
8 8 :created_on: Jan 25, 2011
9 9 :author: marcink
10 10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import os
27 27 import logging
28 28 import traceback
29 29 import shutil
30 30 import datetime
31 31
32 32 from rhodecode.lib.utils2 import LazyProperty
33 33
34 34 from rhodecode.model import BaseModel
35 35 from rhodecode.model.db import RepoGroup, RhodeCodeUi, UserRepoGroupToPerm, \
36 36 User, Permission, UserGroupRepoGroupToPerm, UserGroup, Repository
37 37
38 38 log = logging.getLogger(__name__)
39 39
40 40
41 41 class ReposGroupModel(BaseModel):
42 42
43 43 cls = RepoGroup
44 44
45 45 def _get_user_group(self, users_group):
46 46 return self._get_instance(UserGroup, users_group,
47 47 callback=UserGroup.get_by_group_name)
48 48
49 49 def _get_repo_group(self, repos_group):
50 50 return self._get_instance(RepoGroup, repos_group,
51 51 callback=RepoGroup.get_by_group_name)
52 52
53 53 @LazyProperty
54 54 def repos_path(self):
55 55 """
56 56 Get's the repositories root path from database
57 57 """
58 58
59 59 q = RhodeCodeUi.get_by_key('/')
60 60 return q.ui_value
61 61
62 62 def _create_default_perms(self, new_group):
63 63 # create default permission
64 64 default_perm = 'group.read'
65 65 def_user = User.get_default_user()
66 66 for p in def_user.user_perms:
67 67 if p.permission.permission_name.startswith('group.'):
68 68 default_perm = p.permission.permission_name
69 69 break
70 70
71 71 repo_group_to_perm = UserRepoGroupToPerm()
72 72 repo_group_to_perm.permission = Permission.get_by_key(default_perm)
73 73
74 74 repo_group_to_perm.group = new_group
75 75 repo_group_to_perm.user_id = def_user.user_id
76 76 return repo_group_to_perm
77 77
78 78 def __create_group(self, group_name):
79 79 """
80 80 makes repository group on filesystem
81 81
82 82 :param repo_name:
83 83 :param parent_id:
84 84 """
85 85
86 86 create_path = os.path.join(self.repos_path, group_name)
87 87 log.debug('creating new group in %s' % create_path)
88 88
89 89 if os.path.isdir(create_path):
90 90 raise Exception('That directory already exists !')
91 91
92 92 os.makedirs(create_path)
93 93
94 94 def __rename_group(self, old, new):
95 95 """
96 96 Renames a group on filesystem
97 97
98 98 :param group_name:
99 99 """
100 100
101 101 if old == new:
102 102 log.debug('skipping group rename')
103 103 return
104 104
105 105 log.debug('renaming repository group from %s to %s' % (old, new))
106 106
107 107 old_path = os.path.join(self.repos_path, old)
108 108 new_path = os.path.join(self.repos_path, new)
109 109
110 110 log.debug('renaming repos paths from %s to %s' % (old_path, new_path))
111 111
112 112 if os.path.isdir(new_path):
113 113 raise Exception('Was trying to rename to already '
114 114 'existing dir %s' % new_path)
115 115 shutil.move(old_path, new_path)
116 116
117 117 def __delete_group(self, group, force_delete=False):
118 118 """
119 119 Deletes a group from a filesystem
120 120
121 121 :param group: instance of group from database
122 122 :param force_delete: use shutil rmtree to remove all objects
123 123 """
124 124 paths = group.full_path.split(RepoGroup.url_sep())
125 125 paths = os.sep.join(paths)
126 126
127 127 rm_path = os.path.join(self.repos_path, paths)
128 128 log.info("Removing group %s" % (rm_path))
129 129 # delete only if that path really exists
130 130 if os.path.isdir(rm_path):
131 131 if force_delete:
132 132 shutil.rmtree(rm_path)
133 133 else:
134 134 #archive that group`
135 135 _now = datetime.datetime.now()
136 136 _ms = str(_now.microsecond).rjust(6, '0')
137 137 _d = 'rm__%s_GROUP_%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
138 138 group.name)
139 139 shutil.move(rm_path, os.path.join(self.repos_path, _d))
140 140
141 141 def create(self, group_name, group_description, owner, parent=None, just_db=False):
142 142 try:
143 143 new_repos_group = RepoGroup()
144 144 new_repos_group.user = self._get_user(owner)
145 145 new_repos_group.group_description = group_description or group_name
146 146 new_repos_group.parent_group = self._get_repo_group(parent)
147 147 new_repos_group.group_name = new_repos_group.get_new_name(group_name)
148 148
149 149 self.sa.add(new_repos_group)
150 150 perm_obj = self._create_default_perms(new_repos_group)
151 151 self.sa.add(perm_obj)
152 152
153 153 #create an ADMIN permission for owner, later owner should go into
154 154 #the owner field of groups
155 155 self.grant_user_permission(repos_group=new_repos_group,
156 156 user=owner, perm='group.admin')
157 157
158 158 if not just_db:
159 159 # we need to flush here, in order to check if database won't
160 160 # throw any exceptions, create filesystem dirs at the very end
161 161 self.sa.flush()
162 162 self.__create_group(new_repos_group.group_name)
163 163
164 164 return new_repos_group
165 165 except Exception:
166 166 log.error(traceback.format_exc())
167 167 raise
168 168
169 169 def _update_permissions(self, repos_group, perms_new=None,
170 perms_updates=None, recursive=False):
170 perms_updates=None, recursive=False,
171 check_perms=True):
171 172 from rhodecode.model.repo import RepoModel
172 173 from rhodecode.lib.auth import HasUserGroupPermissionAny
174
173 175 if not perms_new:
174 176 perms_new = []
175 177 if not perms_updates:
176 178 perms_updates = []
177 179
178 180 def _set_perm_user(obj, user, perm):
179 181 if isinstance(obj, RepoGroup):
180 182 self.grant_user_permission(
181 183 repos_group=obj, user=user, perm=perm
182 184 )
183 185 elif isinstance(obj, Repository):
184 186 #we do this ONLY IF repository is non-private
185 187 if obj.private:
186 188 return
187 189
188 190 # we set group permission but we have to switch to repo
189 191 # permission
190 192 perm = perm.replace('group.', 'repository.')
191 193 RepoModel().grant_user_permission(
192 194 repo=obj, user=user, perm=perm
193 195 )
194 196
195 197 def _set_perm_group(obj, users_group, perm):
196 198 if isinstance(obj, RepoGroup):
197 199 self.grant_users_group_permission(
198 200 repos_group=obj, group_name=users_group, perm=perm
199 201 )
200 202 elif isinstance(obj, Repository):
201 203 # we set group permission but we have to switch to repo
202 204 # permission
203 205 perm = perm.replace('group.', 'repository.')
204 206 RepoModel().grant_users_group_permission(
205 207 repo=obj, group_name=users_group, perm=perm
206 208 )
207 209 updates = []
208 210 log.debug('Now updating permissions for %s in recursive mode:%s'
209 211 % (repos_group, recursive))
210 212
211 213 for obj in repos_group.recursive_groups_and_repos():
212 214 #obj is an instance of a group or repositories in that group
213 215 if not recursive:
214 216 obj = repos_group
215 217
216 218 # update permissions
217 219 for member, perm, member_type in perms_updates:
218 220 ## set for user
219 221 if member_type == 'user':
220 222 # this updates also current one if found
221 223 _set_perm_user(obj, user=member, perm=perm)
222 224 ## set for user group
223 225 else:
224 226 #check if we have permissions to alter this usergroup
225 if HasUserGroupPermissionAny('usergroup.read', 'usergroup.write',
226 'usergroup.admin')(member):
227 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
228 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member):
227 229 _set_perm_group(obj, users_group=member, perm=perm)
228 230 # set new permissions
229 231 for member, perm, member_type in perms_new:
230 232 if member_type == 'user':
231 233 _set_perm_user(obj, user=member, perm=perm)
232 234 else:
233 235 #check if we have permissions to alter this usergroup
234 if HasUserGroupPermissionAny('usergroup.read', 'usergroup.write',
235 'usergroup.admin')(member):
236 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
237 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member):
236 238 _set_perm_group(obj, users_group=member, perm=perm)
237 239 updates.append(obj)
238 240 #if it's not recursive call
239 241 # break the loop and don't proceed with other changes
240 242 if not recursive:
241 243 break
242 244 return updates
243 245
244 246 def update(self, repos_group, form_data):
245 247
246 248 try:
247 249 repos_group = self._get_repo_group(repos_group)
248 250 old_path = repos_group.full_path
249 251
250 252 # change properties
251 253 repos_group.group_description = form_data['group_description']
252 254 repos_group.group_parent_id = form_data['group_parent_id']
253 255 repos_group.enable_locking = form_data['enable_locking']
254 256
255 257 repos_group.parent_group = RepoGroup.get(form_data['group_parent_id'])
256 258 repos_group.group_name = repos_group.get_new_name(form_data['group_name'])
257 259 new_path = repos_group.full_path
258 260 self.sa.add(repos_group)
259 261
260 262 # iterate over all members of this groups and do fixes
261 263 # set locking if given
262 264 # if obj is a repoGroup also fix the name of the group according
263 265 # to the parent
264 266 # if obj is a Repo fix it's name
265 267 # this can be potentially heavy operation
266 268 for obj in repos_group.recursive_groups_and_repos():
267 269 #set the value from it's parent
268 270 obj.enable_locking = repos_group.enable_locking
269 271 if isinstance(obj, RepoGroup):
270 272 new_name = obj.get_new_name(obj.name)
271 273 log.debug('Fixing group %s to new name %s' \
272 274 % (obj.group_name, new_name))
273 275 obj.group_name = new_name
274 276 elif isinstance(obj, Repository):
275 277 # we need to get all repositories from this new group and
276 278 # rename them accordingly to new group path
277 279 new_name = obj.get_new_name(obj.just_name)
278 280 log.debug('Fixing repo %s to new name %s' \
279 281 % (obj.repo_name, new_name))
280 282 obj.repo_name = new_name
281 283 self.sa.add(obj)
282 284
283 285 self.__rename_group(old_path, new_path)
284 286
285 287 return repos_group
286 288 except Exception:
287 289 log.error(traceback.format_exc())
288 290 raise
289 291
290 292 def delete(self, repos_group, force_delete=False):
291 293 repos_group = self._get_repo_group(repos_group)
292 294 try:
293 295 self.sa.delete(repos_group)
294 296 self.__delete_group(repos_group, force_delete)
295 297 except Exception:
296 298 log.error('Error removing repos_group %s' % repos_group)
297 299 raise
298 300
299 301 def delete_permission(self, repos_group, obj, obj_type, recursive):
300 302 """
301 303 Revokes permission for repos_group for given obj(user or users_group),
302 304 obj_type can be user or user group
303 305
304 306 :param repos_group:
305 307 :param obj: user or user group id
306 308 :param obj_type: user or user group type
307 309 :param recursive: recurse to all children of group
308 310 """
309 311 from rhodecode.model.repo import RepoModel
310 312 repos_group = self._get_repo_group(repos_group)
311 313
312 314 for el in repos_group.recursive_groups_and_repos():
313 315 if not recursive:
314 316 # if we don't recurse set the permission on only the top level
315 317 # object
316 318 el = repos_group
317 319
318 320 if isinstance(el, RepoGroup):
319 321 if obj_type == 'user':
320 322 ReposGroupModel().revoke_user_permission(el, user=obj)
321 323 elif obj_type == 'users_group':
322 324 ReposGroupModel().revoke_users_group_permission(el, group_name=obj)
323 325 else:
324 326 raise Exception('undefined object type %s' % obj_type)
325 327 elif isinstance(el, Repository):
326 328 if obj_type == 'user':
327 329 RepoModel().revoke_user_permission(el, user=obj)
328 330 elif obj_type == 'users_group':
329 331 RepoModel().revoke_users_group_permission(el, group_name=obj)
330 332 else:
331 333 raise Exception('undefined object type %s' % obj_type)
332 334
333 335 #if it's not recursive call
334 336 # break the loop and don't proceed with other changes
335 337 if not recursive:
336 338 break
337 339
338 340 def grant_user_permission(self, repos_group, user, perm):
339 341 """
340 342 Grant permission for user on given repository group, or update
341 343 existing one if found
342 344
343 345 :param repos_group: Instance of ReposGroup, repositories_group_id,
344 346 or repositories_group name
345 347 :param user: Instance of User, user_id or username
346 348 :param perm: Instance of Permission, or permission_name
347 349 """
348 350
349 351 repos_group = self._get_repo_group(repos_group)
350 352 user = self._get_user(user)
351 353 permission = self._get_perm(perm)
352 354
353 355 # check if we have that permission already
354 356 obj = self.sa.query(UserRepoGroupToPerm)\
355 357 .filter(UserRepoGroupToPerm.user == user)\
356 358 .filter(UserRepoGroupToPerm.group == repos_group)\
357 359 .scalar()
358 360 if obj is None:
359 361 # create new !
360 362 obj = UserRepoGroupToPerm()
361 363 obj.group = repos_group
362 364 obj.user = user
363 365 obj.permission = permission
364 366 self.sa.add(obj)
365 367 log.debug('Granted perm %s to %s on %s' % (perm, user, repos_group))
366 368
367 369 def revoke_user_permission(self, repos_group, user):
368 370 """
369 371 Revoke permission for user on given repository group
370 372
371 373 :param repos_group: Instance of ReposGroup, repositories_group_id,
372 374 or repositories_group name
373 375 :param user: Instance of User, user_id or username
374 376 """
375 377
376 378 repos_group = self._get_repo_group(repos_group)
377 379 user = self._get_user(user)
378 380
379 381 obj = self.sa.query(UserRepoGroupToPerm)\
380 382 .filter(UserRepoGroupToPerm.user == user)\
381 383 .filter(UserRepoGroupToPerm.group == repos_group)\
382 384 .scalar()
383 385 if obj:
384 386 self.sa.delete(obj)
385 387 log.debug('Revoked perm on %s on %s' % (repos_group, user))
386 388
387 389 def grant_users_group_permission(self, repos_group, group_name, perm):
388 390 """
389 391 Grant permission for user group on given repository group, or update
390 392 existing one if found
391 393
392 394 :param repos_group: Instance of ReposGroup, repositories_group_id,
393 395 or repositories_group name
394 396 :param group_name: Instance of UserGroup, users_group_id,
395 397 or user group name
396 398 :param perm: Instance of Permission, or permission_name
397 399 """
398 400 repos_group = self._get_repo_group(repos_group)
399 401 group_name = self._get_user_group(group_name)
400 402 permission = self._get_perm(perm)
401 403
402 404 # check if we have that permission already
403 405 obj = self.sa.query(UserGroupRepoGroupToPerm)\
404 406 .filter(UserGroupRepoGroupToPerm.group == repos_group)\
405 407 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
406 408 .scalar()
407 409
408 410 if obj is None:
409 411 # create new
410 412 obj = UserGroupRepoGroupToPerm()
411 413
412 414 obj.group = repos_group
413 415 obj.users_group = group_name
414 416 obj.permission = permission
415 417 self.sa.add(obj)
416 418 log.debug('Granted perm %s to %s on %s' % (perm, group_name, repos_group))
417 419
418 420 def revoke_users_group_permission(self, repos_group, group_name):
419 421 """
420 422 Revoke permission for user group on given repository group
421 423
422 424 :param repos_group: Instance of ReposGroup, repositories_group_id,
423 425 or repositories_group name
424 426 :param group_name: Instance of UserGroup, users_group_id,
425 427 or user group name
426 428 """
427 429 repos_group = self._get_repo_group(repos_group)
428 430 group_name = self._get_user_group(group_name)
429 431
430 432 obj = self.sa.query(UserGroupRepoGroupToPerm)\
431 433 .filter(UserGroupRepoGroupToPerm.group == repos_group)\
432 434 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
433 435 .scalar()
434 436 if obj:
435 437 self.sa.delete(obj)
436 438 log.debug('Revoked perm to %s on %s' % (repos_group, group_name))
General Comments 0
You need to be logged in to leave comments. Login now