##// END OF EJS Templates
Replaced fallback username from ? to None in hook loggers
marcink -
r4018:727d2a45 default
parent child Browse files
Show More
@@ -1,757 +1,757 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, get_current_rhodecode_user
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 """
119 119 from rhodecode.lib.auth import AuthUser
120 120 user = self._get_user(user)
121 121 repos = AuthUser(user_id=user.user_id).permissions['repositories']
122 122 access_check = lambda r: r[1] in ['repository.read',
123 123 'repository.write',
124 124 'repository.admin']
125 125 repos = [x[0] for x in filter(access_check, repos.items())]
126 126 return Repository.query().filter(Repository.repo_name.in_(repos))
127 127
128 128 def get_users_js(self):
129 129 users = self.sa.query(User).filter(User.active == True).all()
130 130 return json.dumps([
131 131 {
132 132 'id': u.user_id,
133 133 'fname': u.name,
134 134 'lname': u.lastname,
135 135 'nname': u.username,
136 136 'gravatar_lnk': h.gravatar_url(u.email, 14)
137 137 } for u in users]
138 138 )
139 139
140 140 def get_users_groups_js(self):
141 141 users_groups = self.sa.query(UserGroup)\
142 142 .filter(UserGroup.users_group_active == True).all()
143 143 users_groups = UserGroupList(users_groups, perm_set=['usergroup.read',
144 144 'usergroup.write',
145 145 'usergroup.admin'])
146 146 return json.dumps([
147 147 {
148 148 'id': gr.users_group_id,
149 149 'grname': gr.users_group_name,
150 150 'grmembers': len(gr.members),
151 151 } for gr in users_groups]
152 152 )
153 153
154 154 @classmethod
155 155 def _render_datatable(cls, tmpl, *args, **kwargs):
156 156 import rhodecode
157 157 from pylons import tmpl_context as c
158 158 from pylons.i18n.translation import _
159 159
160 160 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
161 161 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
162 162
163 163 tmpl = template.get_def(tmpl)
164 164 kwargs.update(dict(_=_, h=h, c=c))
165 165 return tmpl.render(*args, **kwargs)
166 166
167 167 @classmethod
168 168 def update_repoinfo(cls, repositories=None):
169 169 if not repositories:
170 170 repositories = Repository.getAll()
171 171 for repo in repositories:
172 172 repo.update_changeset_cache()
173 173
174 174 def get_repos_as_dict(self, repos_list=None, admin=False, perm_check=True,
175 175 super_user_actions=False):
176 176 _render = self._render_datatable
177 177 from pylons import tmpl_context as c
178 178
179 179 def quick_menu(repo_name):
180 180 return _render('quick_menu', repo_name)
181 181
182 182 def repo_lnk(name, rtype, private, fork_of):
183 183 return _render('repo_name', name, rtype, private, fork_of,
184 184 short_name=not admin, admin=False)
185 185
186 186 def last_change(last_change):
187 187 return _render("last_change", last_change)
188 188
189 189 def rss_lnk(repo_name):
190 190 return _render("rss", repo_name)
191 191
192 192 def atom_lnk(repo_name):
193 193 return _render("atom", repo_name)
194 194
195 195 def last_rev(repo_name, cs_cache):
196 196 return _render('revision', repo_name, cs_cache.get('revision'),
197 197 cs_cache.get('raw_id'), cs_cache.get('author'),
198 198 cs_cache.get('message'))
199 199
200 200 def desc(desc):
201 201 if c.visual.stylify_metatags:
202 202 return h.urlify_text(h.desc_stylize(h.truncate(desc, 60)))
203 203 else:
204 204 return h.urlify_text(h.truncate(desc, 60))
205 205
206 206 def repo_actions(repo_name):
207 207 return _render('repo_actions', repo_name, super_user_actions)
208 208
209 209 def owner_actions(user_id, username):
210 210 return _render('user_name', user_id, username)
211 211
212 212 repos_data = []
213 213 for repo in repos_list:
214 214 if perm_check:
215 215 # check permission at this level
216 216 if not HasRepoPermissionAny(
217 217 'repository.read', 'repository.write', 'repository.admin'
218 218 )(repo.repo_name, 'get_repos_as_dict check'):
219 219 continue
220 220 cs_cache = repo.changeset_cache
221 221 row = {
222 222 "menu": quick_menu(repo.repo_name),
223 223 "raw_name": repo.repo_name.lower(),
224 224 "name": repo_lnk(repo.repo_name, repo.repo_type,
225 225 repo.private, repo.fork),
226 226 "last_change": last_change(repo.last_db_change),
227 227 "last_changeset": last_rev(repo.repo_name, cs_cache),
228 228 "raw_tip": cs_cache.get('revision'),
229 229 "desc": desc(repo.description),
230 230 "owner": h.person(repo.user.username),
231 231 "rss": rss_lnk(repo.repo_name),
232 232 "atom": atom_lnk(repo.repo_name),
233 233
234 234 }
235 235 if admin:
236 236 row.update({
237 237 "action": repo_actions(repo.repo_name),
238 238 "owner": owner_actions(repo.user.user_id,
239 239 h.person(repo.user.username))
240 240 })
241 241 repos_data.append(row)
242 242
243 243 return {
244 244 "totalRecords": len(repos_list),
245 245 "startIndex": 0,
246 246 "sort": "name",
247 247 "dir": "asc",
248 248 "records": repos_data
249 249 }
250 250
251 251 def _get_defaults(self, repo_name):
252 252 """
253 253 Get's information about repository, and returns a dict for
254 254 usage in forms
255 255
256 256 :param repo_name:
257 257 """
258 258
259 259 repo_info = Repository.get_by_repo_name(repo_name)
260 260
261 261 if repo_info is None:
262 262 return None
263 263
264 264 defaults = repo_info.get_dict()
265 265 group, repo_name, repo_name_full = repo_info.groups_and_repo
266 266 defaults['repo_name'] = repo_name
267 267 defaults['repo_group'] = getattr(group[-1] if group else None,
268 268 'group_id', None)
269 269
270 270 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
271 271 (1, 'repo_description'), (1, 'repo_enable_locking'),
272 272 (1, 'repo_landing_rev'), (0, 'clone_uri'),
273 273 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
274 274 attr = k
275 275 if strip:
276 276 attr = remove_prefix(k, 'repo_')
277 277
278 278 defaults[k] = defaults[attr]
279 279
280 280 # fill owner
281 281 if repo_info.user:
282 282 defaults.update({'user': repo_info.user.username})
283 283 else:
284 284 replacement_user = User.query().filter(User.admin ==
285 285 True).first().username
286 286 defaults.update({'user': replacement_user})
287 287
288 288 # fill repository users
289 289 for p in repo_info.repo_to_perm:
290 290 defaults.update({'u_perm_%s' % p.user.username:
291 291 p.permission.permission_name})
292 292
293 293 # fill repository groups
294 294 for p in repo_info.users_group_to_perm:
295 295 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
296 296 p.permission.permission_name})
297 297
298 298 return defaults
299 299
300 300 def update(self, org_repo_name, **kwargs):
301 301 try:
302 302 cur_repo = self.get_by_repo_name(org_repo_name, cache=False)
303 303
304 304 if 'user' in kwargs:
305 305 cur_repo.user = User.get_by_username(kwargs['user'])
306 306
307 307 if 'repo_group' in kwargs:
308 308 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
309 309
310 310 for strip, k in [(1, 'repo_enable_downloads'),
311 311 (1, 'repo_description'), (1, 'repo_enable_locking'),
312 312 (1, 'repo_landing_rev'), (0, 'clone_uri'),
313 313 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
314 314 if k in kwargs:
315 315 val = kwargs[k]
316 316 if strip:
317 317 k = remove_prefix(k, 'repo_')
318 318 setattr(cur_repo, k, val)
319 319
320 320 new_name = cur_repo.get_new_name(kwargs['repo_name'])
321 321 cur_repo.repo_name = new_name
322 322 #if private flag is set, reset default permission to NONE
323 323
324 324 if kwargs.get('repo_private'):
325 325 EMPTY_PERM = 'repository.none'
326 326 RepoModel().grant_user_permission(
327 327 repo=cur_repo, user='default', perm=EMPTY_PERM
328 328 )
329 329 #handle extra fields
330 330 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX), kwargs):
331 331 k = RepositoryField.un_prefix_key(field)
332 332 ex_field = RepositoryField.get_by_key_name(key=k, repo=cur_repo)
333 333 if ex_field:
334 334 ex_field.field_value = kwargs[field]
335 335 self.sa.add(ex_field)
336 336 self.sa.add(cur_repo)
337 337
338 338 if org_repo_name != new_name:
339 339 # rename repository
340 340 self.__rename_repo(old=org_repo_name, new=new_name)
341 341
342 342 return cur_repo
343 343 except Exception:
344 344 log.error(traceback.format_exc())
345 345 raise
346 346
347 347 def create_repo(self, repo_name, repo_type, description, owner,
348 348 private=False, clone_uri=None, repos_group=None,
349 349 landing_rev='tip', just_db=False, fork_of=None,
350 350 copy_fork_permissions=False, enable_statistics=False,
351 351 enable_locking=False, enable_downloads=False):
352 352 """
353 353 Create repository
354 354
355 355 """
356 356 from rhodecode.model.scm import ScmModel
357 357
358 358 owner = self._get_user(owner)
359 359 fork_of = self._get_repo(fork_of)
360 360 repos_group = self._get_repo_group(repos_group)
361 361 try:
362 362
363 363 # repo name is just a name of repository
364 364 # while repo_name_full is a full qualified name that is combined
365 365 # with name and path of group
366 366 repo_name_full = repo_name
367 367 repo_name = repo_name.split(self.URL_SEPARATOR)[-1]
368 368
369 369 new_repo = Repository()
370 370 new_repo.enable_statistics = False
371 371 new_repo.repo_name = repo_name_full
372 372 new_repo.repo_type = repo_type
373 373 new_repo.user = owner
374 374 new_repo.group = repos_group
375 375 new_repo.description = description or repo_name
376 376 new_repo.private = private
377 377 new_repo.clone_uri = clone_uri
378 378 new_repo.landing_rev = landing_rev
379 379
380 380 new_repo.enable_statistics = enable_statistics
381 381 new_repo.enable_locking = enable_locking
382 382 new_repo.enable_downloads = enable_downloads
383 383
384 384 if repos_group:
385 385 new_repo.enable_locking = repos_group.enable_locking
386 386
387 387 if fork_of:
388 388 parent_repo = fork_of
389 389 new_repo.fork = parent_repo
390 390
391 391 self.sa.add(new_repo)
392 392
393 393 if fork_of:
394 394 if copy_fork_permissions:
395 395 repo = fork_of
396 396 user_perms = UserRepoToPerm.query()\
397 397 .filter(UserRepoToPerm.repository == repo).all()
398 398 group_perms = UserGroupRepoToPerm.query()\
399 399 .filter(UserGroupRepoToPerm.repository == repo).all()
400 400
401 401 for perm in user_perms:
402 402 UserRepoToPerm.create(perm.user, new_repo,
403 403 perm.permission)
404 404
405 405 for perm in group_perms:
406 406 UserGroupRepoToPerm.create(perm.users_group, new_repo,
407 407 perm.permission)
408 408 else:
409 409 perm_obj = self._create_default_perms(new_repo, private)
410 410 self.sa.add(perm_obj)
411 411 else:
412 412 perm_obj = self._create_default_perms(new_repo, private)
413 413 self.sa.add(perm_obj)
414 414
415 415 if not just_db:
416 416 self.__create_repo(repo_name, repo_type,
417 417 repos_group,
418 418 clone_uri)
419 419 log_create_repository(new_repo.get_dict(),
420 420 created_by=owner.username)
421 421
422 422 # now automatically start following this repository as owner
423 423 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
424 424 owner.user_id)
425 425 return new_repo
426 426 except Exception:
427 427 log.error(traceback.format_exc())
428 428 raise
429 429
430 430 def create(self, form_data, cur_user, just_db=False, fork=None):
431 431 """
432 432 Backward compatibility function, just a wrapper on top of create_repo
433 433
434 434 :param form_data:
435 435 :param cur_user:
436 436 :param just_db:
437 437 :param fork:
438 438 """
439 439 owner = cur_user
440 440 repo_name = form_data['repo_name_full']
441 441 repo_type = form_data['repo_type']
442 442 description = form_data['repo_description']
443 443 private = form_data['repo_private']
444 444 clone_uri = form_data.get('clone_uri')
445 445 repos_group = form_data['repo_group']
446 446 landing_rev = form_data['repo_landing_rev']
447 447 copy_fork_permissions = form_data.get('copy_permissions')
448 448 fork_of = form_data.get('fork_parent_id')
449 449
450 450 ## repo creation defaults, private and repo_type are filled in form
451 451 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
452 452 enable_statistics = defs.get('repo_enable_statistics')
453 453 enable_locking = defs.get('repo_enable_locking')
454 454 enable_downloads = defs.get('repo_enable_downloads')
455 455
456 456 return self.create_repo(
457 457 repo_name, repo_type, description, owner, private, clone_uri,
458 458 repos_group, landing_rev, just_db, fork_of, copy_fork_permissions,
459 459 enable_statistics, enable_locking, enable_downloads
460 460 )
461 461
462 462 def _update_permissions(self, repo, perms_new=None, perms_updates=None,
463 463 check_perms=True):
464 464 if not perms_new:
465 465 perms_new = []
466 466 if not perms_updates:
467 467 perms_updates = []
468 468
469 469 # update permissions
470 470 for member, perm, member_type in perms_updates:
471 471 if member_type == 'user':
472 472 # this updates existing one
473 473 self.grant_user_permission(
474 474 repo=repo, user=member, perm=perm
475 475 )
476 476 else:
477 477 #check if we have permissions to alter this usergroup
478 478 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
479 479 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member):
480 480 self.grant_users_group_permission(
481 481 repo=repo, group_name=member, perm=perm
482 482 )
483 483 # set new permissions
484 484 for member, perm, member_type in perms_new:
485 485 if member_type == 'user':
486 486 self.grant_user_permission(
487 487 repo=repo, user=member, perm=perm
488 488 )
489 489 else:
490 490 #check if we have permissions to alter this usergroup
491 491 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
492 492 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member):
493 493 self.grant_users_group_permission(
494 494 repo=repo, group_name=member, perm=perm
495 495 )
496 496
497 497 def create_fork(self, form_data, cur_user):
498 498 """
499 499 Simple wrapper into executing celery task for fork creation
500 500
501 501 :param form_data:
502 502 :param cur_user:
503 503 """
504 504 from rhodecode.lib.celerylib import tasks, run_task
505 505 run_task(tasks.create_repo_fork, form_data, cur_user)
506 506
507 507 def delete(self, repo, forks=None, fs_remove=True, cur_user=None):
508 508 """
509 509 Delete given repository, forks parameter defines what do do with
510 510 attached forks. Throws AttachedForksError if deleted repo has attached
511 511 forks
512 512
513 513 :param repo:
514 514 :param forks: str 'delete' or 'detach'
515 515 :param fs_remove: remove(archive) repo from filesystem
516 516 """
517 517 if not cur_user:
518 cur_user = getattr(get_current_rhodecode_user(), 'username', '?')
518 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
519 519 repo = self._get_repo(repo)
520 520 if repo:
521 521 if forks == 'detach':
522 522 for r in repo.forks:
523 523 r.fork = None
524 524 self.sa.add(r)
525 525 elif forks == 'delete':
526 526 for r in repo.forks:
527 527 self.delete(r, forks='delete')
528 528 elif [f for f in repo.forks]:
529 529 raise AttachedForksError()
530 530
531 531 old_repo_dict = repo.get_dict()
532 532 owner = repo.user
533 533 try:
534 534 self.sa.delete(repo)
535 535 if fs_remove:
536 536 self.__delete_repo(repo)
537 537 else:
538 538 log.debug('skipping removal from filesystem')
539 539 log_delete_repository(old_repo_dict,
540 540 deleted_by=cur_user)
541 541 except Exception:
542 542 log.error(traceback.format_exc())
543 543 raise
544 544
545 545 def grant_user_permission(self, repo, user, perm):
546 546 """
547 547 Grant permission for user on given repository, or update existing one
548 548 if found
549 549
550 550 :param repo: Instance of Repository, repository_id, or repository name
551 551 :param user: Instance of User, user_id or username
552 552 :param perm: Instance of Permission, or permission_name
553 553 """
554 554 user = self._get_user(user)
555 555 repo = self._get_repo(repo)
556 556 permission = self._get_perm(perm)
557 557
558 558 # check if we have that permission already
559 559 obj = self.sa.query(UserRepoToPerm)\
560 560 .filter(UserRepoToPerm.user == user)\
561 561 .filter(UserRepoToPerm.repository == repo)\
562 562 .scalar()
563 563 if obj is None:
564 564 # create new !
565 565 obj = UserRepoToPerm()
566 566 obj.repository = repo
567 567 obj.user = user
568 568 obj.permission = permission
569 569 self.sa.add(obj)
570 570 log.debug('Granted perm %s to %s on %s' % (perm, user, repo))
571 571
572 572 def revoke_user_permission(self, repo, user):
573 573 """
574 574 Revoke permission for user on given repository
575 575
576 576 :param repo: Instance of Repository, repository_id, or repository name
577 577 :param user: Instance of User, user_id or username
578 578 """
579 579
580 580 user = self._get_user(user)
581 581 repo = self._get_repo(repo)
582 582
583 583 obj = self.sa.query(UserRepoToPerm)\
584 584 .filter(UserRepoToPerm.repository == repo)\
585 585 .filter(UserRepoToPerm.user == user)\
586 586 .scalar()
587 587 if obj:
588 588 self.sa.delete(obj)
589 589 log.debug('Revoked perm on %s on %s' % (repo, user))
590 590
591 591 def grant_users_group_permission(self, repo, group_name, perm):
592 592 """
593 593 Grant permission for user group on given repository, or update
594 594 existing one if found
595 595
596 596 :param repo: Instance of Repository, repository_id, or repository name
597 597 :param group_name: Instance of UserGroup, users_group_id,
598 598 or user group name
599 599 :param perm: Instance of Permission, or permission_name
600 600 """
601 601 repo = self._get_repo(repo)
602 602 group_name = self._get_user_group(group_name)
603 603 permission = self._get_perm(perm)
604 604
605 605 # check if we have that permission already
606 606 obj = self.sa.query(UserGroupRepoToPerm)\
607 607 .filter(UserGroupRepoToPerm.users_group == group_name)\
608 608 .filter(UserGroupRepoToPerm.repository == repo)\
609 609 .scalar()
610 610
611 611 if obj is None:
612 612 # create new
613 613 obj = UserGroupRepoToPerm()
614 614
615 615 obj.repository = repo
616 616 obj.users_group = group_name
617 617 obj.permission = permission
618 618 self.sa.add(obj)
619 619 log.debug('Granted perm %s to %s on %s' % (perm, group_name, repo))
620 620
621 621 def revoke_users_group_permission(self, repo, group_name):
622 622 """
623 623 Revoke permission for user group on given repository
624 624
625 625 :param repo: Instance of Repository, repository_id, or repository name
626 626 :param group_name: Instance of UserGroup, users_group_id,
627 627 or user group name
628 628 """
629 629 repo = self._get_repo(repo)
630 630 group_name = self._get_user_group(group_name)
631 631
632 632 obj = self.sa.query(UserGroupRepoToPerm)\
633 633 .filter(UserGroupRepoToPerm.repository == repo)\
634 634 .filter(UserGroupRepoToPerm.users_group == group_name)\
635 635 .scalar()
636 636 if obj:
637 637 self.sa.delete(obj)
638 638 log.debug('Revoked perm to %s on %s' % (repo, group_name))
639 639
640 640 def delete_stats(self, repo_name):
641 641 """
642 642 removes stats for given repo
643 643
644 644 :param repo_name:
645 645 """
646 646 repo = self._get_repo(repo_name)
647 647 try:
648 648 obj = self.sa.query(Statistics)\
649 649 .filter(Statistics.repository == repo).scalar()
650 650 if obj:
651 651 self.sa.delete(obj)
652 652 except Exception:
653 653 log.error(traceback.format_exc())
654 654 raise
655 655
656 656 def _create_repo(self, repo_name, alias, parent, clone_uri=False,
657 657 repo_store_location=None):
658 658 return self.__create_repo(repo_name, alias, parent, clone_uri,
659 659 repo_store_location)
660 660
661 661 def __create_repo(self, repo_name, alias, parent, clone_uri=False,
662 662 repo_store_location=None):
663 663 """
664 664 makes repository on filesystem. It's group aware means it'll create
665 665 a repository within a group, and alter the paths accordingly of
666 666 group location
667 667
668 668 :param repo_name:
669 669 :param alias:
670 670 :param parent_id:
671 671 :param clone_uri:
672 672 :param repo_path:
673 673 """
674 674 from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
675 675 from rhodecode.model.scm import ScmModel
676 676
677 677 if parent:
678 678 new_parent_path = os.sep.join(parent.full_path_splitted)
679 679 else:
680 680 new_parent_path = ''
681 681 if repo_store_location:
682 682 _paths = [repo_store_location]
683 683 else:
684 684 _paths = [self.repos_path, new_parent_path, repo_name]
685 685 # we need to make it str for mercurial
686 686 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
687 687
688 688 # check if this path is not a repository
689 689 if is_valid_repo(repo_path, self.repos_path):
690 690 raise Exception('This path %s is a valid repository' % repo_path)
691 691
692 692 # check if this path is a group
693 693 if is_valid_repos_group(repo_path, self.repos_path):
694 694 raise Exception('This path %s is a valid group' % repo_path)
695 695
696 696 log.info('creating repo %s in %s @ %s' % (
697 697 repo_name, safe_unicode(repo_path),
698 698 obfuscate_url_pw(clone_uri)
699 699 )
700 700 )
701 701 backend = get_backend(alias)
702 702 if alias == 'hg':
703 703 repo = backend(repo_path, create=True, src_url=clone_uri)
704 704 elif alias == 'git':
705 705 repo = backend(repo_path, create=True, src_url=clone_uri, bare=True)
706 706 # add rhodecode hook into this repo
707 707 ScmModel().install_git_hook(repo=repo)
708 708 else:
709 709 raise Exception('Undefined alias %s' % alias)
710 710 return repo
711 711
712 712 def __rename_repo(self, old, new):
713 713 """
714 714 renames repository on filesystem
715 715
716 716 :param old: old name
717 717 :param new: new name
718 718 """
719 719 log.info('renaming repo from %s to %s' % (old, new))
720 720
721 721 old_path = os.path.join(self.repos_path, old)
722 722 new_path = os.path.join(self.repos_path, new)
723 723 if os.path.isdir(new_path):
724 724 raise Exception(
725 725 'Was trying to rename to already existing dir %s' % new_path
726 726 )
727 727 shutil.move(old_path, new_path)
728 728
729 729 def __delete_repo(self, repo):
730 730 """
731 731 removes repo from filesystem, the removal is acctually made by
732 732 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
733 733 repository is no longer valid for rhodecode, can be undeleted later on
734 734 by reverting the renames on this repository
735 735
736 736 :param repo: repo object
737 737 """
738 738 rm_path = os.path.join(self.repos_path, repo.repo_name)
739 739 log.info("Removing %s" % (rm_path))
740 740 # disable hg/git internal that it doesn't get detected as repo
741 741 alias = repo.repo_type
742 742
743 743 bare = getattr(repo.scm_instance, 'bare', False)
744 744
745 745 if not bare:
746 746 # skip this for bare git repos
747 747 shutil.move(os.path.join(rm_path, '.%s' % alias),
748 748 os.path.join(rm_path, 'rm__.%s' % alias))
749 749 # disable repo
750 750 _now = datetime.now()
751 751 _ms = str(_now.microsecond).rjust(6, '0')
752 752 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
753 753 repo.just_name)
754 754 if repo.group:
755 755 args = repo.group.full_path_splitted + [_d]
756 756 _d = os.path.join(*args)
757 757 shutil.move(rm_path, os.path.join(self.repos_path, _d))
@@ -1,829 +1,829 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.user
4 4 ~~~~~~~~~~~~~~~~~~~~
5 5
6 6 users model for RhodeCode
7 7
8 8 :created_on: Apr 9, 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
26 26 import logging
27 27 import traceback
28 28 import itertools
29 29 import collections
30 30 from pylons import url
31 31 from pylons.i18n.translation import _
32 32
33 33 from sqlalchemy.exc import DatabaseError
34 34 from sqlalchemy.orm import joinedload
35 35
36 36 from rhodecode.lib.utils2 import safe_unicode, generate_api_key, get_current_rhodecode_user
37 37 from rhodecode.lib.caching_query import FromCache
38 38 from rhodecode.model import BaseModel
39 39 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
40 40 UserToPerm, UserGroupRepoToPerm, UserGroupToPerm, UserGroupMember, \
41 41 Notification, RepoGroup, UserRepoGroupToPerm, UserGroupRepoGroupToPerm, \
42 42 UserEmailMap, UserIpMap, UserGroupUserGroupToPerm, UserGroup
43 43 from rhodecode.lib.exceptions import DefaultUserException, \
44 44 UserOwnsReposException
45 45 from rhodecode.model.meta import Session
46 46
47 47
48 48 log = logging.getLogger(__name__)
49 49
50 50 PERM_WEIGHTS = Permission.PERM_WEIGHTS
51 51
52 52
53 53 class UserModel(BaseModel):
54 54 cls = User
55 55
56 56 def get(self, user_id, cache=False):
57 57 user = self.sa.query(User)
58 58 if cache:
59 59 user = user.options(FromCache("sql_cache_short",
60 60 "get_user_%s" % user_id))
61 61 return user.get(user_id)
62 62
63 63 def get_user(self, user):
64 64 return self._get_user(user)
65 65
66 66 def get_by_username(self, username, cache=False, case_insensitive=False):
67 67
68 68 if case_insensitive:
69 69 user = self.sa.query(User).filter(User.username.ilike(username))
70 70 else:
71 71 user = self.sa.query(User)\
72 72 .filter(User.username == username)
73 73 if cache:
74 74 user = user.options(FromCache("sql_cache_short",
75 75 "get_user_%s" % username))
76 76 return user.scalar()
77 77
78 78 def get_by_email(self, email, cache=False, case_insensitive=False):
79 79 return User.get_by_email(email, case_insensitive, cache)
80 80
81 81 def get_by_api_key(self, api_key, cache=False):
82 82 return User.get_by_api_key(api_key, cache)
83 83
84 84 def create(self, form_data, cur_user=None):
85 85 if not cur_user:
86 cur_user = getattr(get_current_rhodecode_user(), 'username', '?')
86 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
87 87 from rhodecode.lib.auth import get_crypt_password
88 88 try:
89 89 new_user = User()
90 90 for k, v in form_data.items():
91 91 if k == 'password':
92 92 v = get_crypt_password(v)
93 93 if k == 'firstname':
94 94 k = 'name'
95 95 setattr(new_user, k, v)
96 96
97 97 new_user.api_key = generate_api_key(form_data['username'])
98 98 self.sa.add(new_user)
99 99
100 100 from rhodecode.lib.hooks import log_create_user
101 101 log_create_user(new_user.get_dict(), cur_user)
102 102 return new_user
103 103 except Exception:
104 104 log.error(traceback.format_exc())
105 105 raise
106 106
107 107 def create_or_update(self, username, password, email, firstname='',
108 108 lastname='', active=True, admin=False, ldap_dn=None, cur_user=None):
109 109 """
110 110 Creates a new instance if not found, or updates current one
111 111
112 112 :param username:
113 113 :param password:
114 114 :param email:
115 115 :param active:
116 116 :param firstname:
117 117 :param lastname:
118 118 :param active:
119 119 :param admin:
120 120 :param ldap_dn:
121 121 """
122 122 if not cur_user:
123 cur_user = getattr(get_current_rhodecode_user(), 'username', '?')
123 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
124 124
125 125 from rhodecode.lib.auth import get_crypt_password
126 126
127 127 log.debug('Checking for %s account in RhodeCode database' % username)
128 128 user = User.get_by_username(username, case_insensitive=True)
129 129 if user is None:
130 130 log.debug('creating new user %s' % username)
131 131 new_user = User()
132 132 edit = False
133 133 else:
134 134 log.debug('updating user %s' % username)
135 135 new_user = user
136 136 edit = True
137 137
138 138 try:
139 139 new_user.username = username
140 140 new_user.admin = admin
141 141 # set password only if creating an user or password is changed
142 142 if not edit or user.password != password:
143 143 new_user.password = get_crypt_password(password) if password else None
144 144 new_user.api_key = generate_api_key(username)
145 145 new_user.email = email
146 146 new_user.active = active
147 147 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
148 148 new_user.name = firstname
149 149 new_user.lastname = lastname
150 150 self.sa.add(new_user)
151 151
152 152 if not edit:
153 153 from rhodecode.lib.hooks import log_create_user
154 154 log_create_user(new_user.get_dict(), cur_user)
155 155 return new_user
156 156 except (DatabaseError,):
157 157 log.error(traceback.format_exc())
158 158 raise
159 159
160 160 def create_for_container_auth(self, username, attrs, cur_user=None):
161 161 """
162 162 Creates the given user if it's not already in the database
163 163
164 164 :param username:
165 165 :param attrs:
166 166 """
167 167 if not cur_user:
168 cur_user = getattr(get_current_rhodecode_user(), 'username', '?')
168 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
169 169 if self.get_by_username(username, case_insensitive=True) is None:
170 170
171 171 # autogenerate email for container account without one
172 172 generate_email = lambda usr: '%s@container_auth.account' % usr
173 173
174 174 try:
175 175 new_user = User()
176 176 new_user.username = username
177 177 new_user.password = None
178 178 new_user.api_key = generate_api_key(username)
179 179 new_user.email = attrs['email']
180 180 new_user.active = attrs.get('active', True)
181 181 new_user.name = attrs['name'] or generate_email(username)
182 182 new_user.lastname = attrs['lastname']
183 183
184 184 self.sa.add(new_user)
185 185
186 186 from rhodecode.lib.hooks import log_create_user
187 187 log_create_user(new_user.get_dict(), cur_user)
188 188 return new_user
189 189 except (DatabaseError,):
190 190 log.error(traceback.format_exc())
191 191 self.sa.rollback()
192 192 raise
193 193 log.debug('User %s already exists. Skipping creation of account'
194 194 ' for container auth.', username)
195 195 return None
196 196
197 197 def create_ldap(self, username, password, user_dn, attrs, cur_user=None):
198 198 """
199 199 Checks if user is in database, if not creates this user marked
200 200 as ldap user
201 201
202 202 :param username:
203 203 :param password:
204 204 :param user_dn:
205 205 :param attrs:
206 206 """
207 207 if not cur_user:
208 cur_user = getattr(get_current_rhodecode_user(), 'username', '?')
208 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
209 209 from rhodecode.lib.auth import get_crypt_password
210 210 log.debug('Checking for such ldap account in RhodeCode database')
211 211 if self.get_by_username(username, case_insensitive=True) is None:
212 212
213 213 # autogenerate email for ldap account without one
214 214 generate_email = lambda usr: '%s@ldap.account' % usr
215 215
216 216 try:
217 217 new_user = User()
218 218 username = username.lower()
219 219 # add ldap account always lowercase
220 220 new_user.username = username
221 221 new_user.password = get_crypt_password(password)
222 222 new_user.api_key = generate_api_key(username)
223 223 new_user.email = attrs['email'] or generate_email(username)
224 224 new_user.active = attrs.get('active', True)
225 225 new_user.ldap_dn = safe_unicode(user_dn)
226 226 new_user.name = attrs['name']
227 227 new_user.lastname = attrs['lastname']
228 228
229 229 self.sa.add(new_user)
230 230
231 231 from rhodecode.lib.hooks import log_create_user
232 232 log_create_user(new_user.get_dict(), cur_user)
233 233 return new_user
234 234 except (DatabaseError,):
235 235 log.error(traceback.format_exc())
236 236 self.sa.rollback()
237 237 raise
238 238 log.debug('this %s user exists skipping creation of ldap account',
239 239 username)
240 240 return None
241 241
242 242 def create_registration(self, form_data):
243 243 from rhodecode.model.notification import NotificationModel
244 244
245 245 try:
246 246 form_data['admin'] = False
247 247 new_user = self.create(form_data)
248 248
249 249 self.sa.add(new_user)
250 250 self.sa.flush()
251 251
252 252 # notification to admins
253 253 subject = _('New user registration')
254 254 body = ('New user registration\n'
255 255 '---------------------\n'
256 256 '- Username: %s\n'
257 257 '- Full Name: %s\n'
258 258 '- Email: %s\n')
259 259 body = body % (new_user.username, new_user.full_name,
260 260 new_user.email)
261 261 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
262 262 kw = {'registered_user_url': edit_url}
263 263 NotificationModel().create(created_by=new_user, subject=subject,
264 264 body=body, recipients=None,
265 265 type_=Notification.TYPE_REGISTRATION,
266 266 email_kwargs=kw)
267 267
268 268 except Exception:
269 269 log.error(traceback.format_exc())
270 270 raise
271 271
272 272 def update(self, user_id, form_data, skip_attrs=[]):
273 273 from rhodecode.lib.auth import get_crypt_password
274 274 try:
275 275 user = self.get(user_id, cache=False)
276 276 if user.username == 'default':
277 277 raise DefaultUserException(
278 278 _("You can't Edit this user since it's"
279 279 " crucial for entire application"))
280 280
281 281 for k, v in form_data.items():
282 282 if k in skip_attrs:
283 283 continue
284 284 if k == 'new_password' and v:
285 285 user.password = get_crypt_password(v)
286 286 user.api_key = generate_api_key(user.username)
287 287 else:
288 288 if k == 'firstname':
289 289 k = 'name'
290 290 setattr(user, k, v)
291 291 self.sa.add(user)
292 292 except Exception:
293 293 log.error(traceback.format_exc())
294 294 raise
295 295
296 296 def update_user(self, user, **kwargs):
297 297 from rhodecode.lib.auth import get_crypt_password
298 298 try:
299 299 user = self._get_user(user)
300 300 if user.username == 'default':
301 301 raise DefaultUserException(
302 302 _("You can't Edit this user since it's"
303 303 " crucial for entire application")
304 304 )
305 305
306 306 for k, v in kwargs.items():
307 307 if k == 'password' and v:
308 308 v = get_crypt_password(v)
309 309 user.api_key = generate_api_key(user.username)
310 310
311 311 setattr(user, k, v)
312 312 self.sa.add(user)
313 313 return user
314 314 except Exception:
315 315 log.error(traceback.format_exc())
316 316 raise
317 317
318 318 def delete(self, user, cur_user=None):
319 319 if not cur_user:
320 cur_user = getattr(get_current_rhodecode_user(), 'username', '?')
320 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
321 321 user = self._get_user(user)
322 322
323 323 try:
324 324 if user.username == 'default':
325 325 raise DefaultUserException(
326 326 _(u"You can't remove this user since it's"
327 327 " crucial for entire application")
328 328 )
329 329 if user.repositories:
330 330 repos = [x.repo_name for x in user.repositories]
331 331 raise UserOwnsReposException(
332 332 _(u'user "%s" still owns %s repositories and cannot be '
333 333 'removed. Switch owners or remove those repositories. %s')
334 334 % (user.username, len(repos), ', '.join(repos))
335 335 )
336 336 self.sa.delete(user)
337 337
338 338 from rhodecode.lib.hooks import log_delete_user
339 339 log_delete_user(user.get_dict(), cur_user)
340 340 except Exception:
341 341 log.error(traceback.format_exc())
342 342 raise
343 343
344 344 def reset_password_link(self, data):
345 345 from rhodecode.lib.celerylib import tasks, run_task
346 346 from rhodecode.model.notification import EmailNotificationModel
347 347 user_email = data['email']
348 348 try:
349 349 user = User.get_by_email(user_email)
350 350 if user:
351 351 log.debug('password reset user found %s' % user)
352 352 link = url('reset_password_confirmation', key=user.api_key,
353 353 qualified=True)
354 354 reg_type = EmailNotificationModel.TYPE_PASSWORD_RESET
355 355 body = EmailNotificationModel().get_email_tmpl(reg_type,
356 356 **{'user': user.short_contact,
357 357 'reset_url': link})
358 358 log.debug('sending email')
359 359 run_task(tasks.send_email, user_email,
360 360 _("Password reset link"), body, body)
361 361 log.info('send new password mail to %s' % user_email)
362 362 else:
363 363 log.debug("password reset email %s not found" % user_email)
364 364 except Exception:
365 365 log.error(traceback.format_exc())
366 366 return False
367 367
368 368 return True
369 369
370 370 def reset_password(self, data):
371 371 from rhodecode.lib.celerylib import tasks, run_task
372 372 from rhodecode.lib import auth
373 373 user_email = data['email']
374 374 try:
375 375 try:
376 376 user = User.get_by_email(user_email)
377 377 new_passwd = auth.PasswordGenerator().gen_password(8,
378 378 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
379 379 if user:
380 380 user.password = auth.get_crypt_password(new_passwd)
381 381 user.api_key = auth.generate_api_key(user.username)
382 382 Session().add(user)
383 383 Session().commit()
384 384 log.info('change password for %s' % user_email)
385 385 if new_passwd is None:
386 386 raise Exception('unable to generate new password')
387 387 except Exception:
388 388 log.error(traceback.format_exc())
389 389 Session().rollback()
390 390
391 391 run_task(tasks.send_email, user_email,
392 392 _('Your new password'),
393 393 _('Your new RhodeCode password:%s') % (new_passwd))
394 394 log.info('send new password mail to %s' % user_email)
395 395
396 396 except Exception:
397 397 log.error('Failed to update user password')
398 398 log.error(traceback.format_exc())
399 399
400 400 return True
401 401
402 402 def fill_data(self, auth_user, user_id=None, api_key=None):
403 403 """
404 404 Fetches auth_user by user_id,or api_key if present.
405 405 Fills auth_user attributes with those taken from database.
406 406 Additionally set's is_authenitated if lookup fails
407 407 present in database
408 408
409 409 :param auth_user: instance of user to set attributes
410 410 :param user_id: user id to fetch by
411 411 :param api_key: api key to fetch by
412 412 """
413 413 if user_id is None and api_key is None:
414 414 raise Exception('You need to pass user_id or api_key')
415 415
416 416 try:
417 417 if api_key:
418 418 dbuser = self.get_by_api_key(api_key)
419 419 else:
420 420 dbuser = self.get(user_id)
421 421
422 422 if dbuser is not None and dbuser.active:
423 423 log.debug('filling %s data' % dbuser)
424 424 for k, v in dbuser.get_dict().items():
425 425 setattr(auth_user, k, v)
426 426 else:
427 427 return False
428 428
429 429 except Exception:
430 430 log.error(traceback.format_exc())
431 431 auth_user.is_authenticated = False
432 432 return False
433 433
434 434 return True
435 435
436 436 def fill_perms(self, user, explicit=True, algo='higherwin'):
437 437 """
438 438 Fills user permission attribute with permissions taken from database
439 439 works for permissions given for repositories, and for permissions that
440 440 are granted to groups
441 441
442 442 :param user: user instance to fill his perms
443 443 :param explicit: In case there are permissions both for user and a group
444 444 that user is part of, explicit flag will defiine if user will
445 445 explicitly override permissions from group, if it's False it will
446 446 make decision based on the algo
447 447 :param algo: algorithm to decide what permission should be choose if
448 448 it's multiple defined, eg user in two different groups. It also
449 449 decides if explicit flag is turned off how to specify the permission
450 450 for case when user is in a group + have defined separate permission
451 451 """
452 452 RK = 'repositories'
453 453 GK = 'repositories_groups'
454 454 UK = 'user_groups'
455 455 GLOBAL = 'global'
456 456 user.permissions[RK] = {}
457 457 user.permissions[GK] = {}
458 458 user.permissions[UK] = {}
459 459 user.permissions[GLOBAL] = set()
460 460
461 461 def _choose_perm(new_perm, cur_perm):
462 462 new_perm_val = PERM_WEIGHTS[new_perm]
463 463 cur_perm_val = PERM_WEIGHTS[cur_perm]
464 464 if algo == 'higherwin':
465 465 if new_perm_val > cur_perm_val:
466 466 return new_perm
467 467 return cur_perm
468 468 elif algo == 'lowerwin':
469 469 if new_perm_val < cur_perm_val:
470 470 return new_perm
471 471 return cur_perm
472 472
473 473 #======================================================================
474 474 # fetch default permissions
475 475 #======================================================================
476 476 default_user = User.get_by_username('default', cache=True)
477 477 default_user_id = default_user.user_id
478 478
479 479 default_repo_perms = Permission.get_default_perms(default_user_id)
480 480 default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
481 481 default_user_group_perms = Permission.get_default_user_group_perms(default_user_id)
482 482
483 483 if user.is_admin:
484 484 #==================================================================
485 485 # admin user have all default rights for repositories
486 486 # and groups set to admin
487 487 #==================================================================
488 488 user.permissions[GLOBAL].add('hg.admin')
489 489
490 490 # repositories
491 491 for perm in default_repo_perms:
492 492 r_k = perm.UserRepoToPerm.repository.repo_name
493 493 p = 'repository.admin'
494 494 user.permissions[RK][r_k] = p
495 495
496 496 # repository groups
497 497 for perm in default_repo_groups_perms:
498 498 rg_k = perm.UserRepoGroupToPerm.group.group_name
499 499 p = 'group.admin'
500 500 user.permissions[GK][rg_k] = p
501 501
502 502 # user groups
503 503 for perm in default_user_group_perms:
504 504 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
505 505 p = 'usergroup.admin'
506 506 user.permissions[UK][u_k] = p
507 507 return user
508 508
509 509 #==================================================================
510 510 # SET DEFAULTS GLOBAL, REPOS, REPOSITORY GROUPS
511 511 #==================================================================
512 512 uid = user.user_id
513 513
514 514 # default global permissions taken fron the default user
515 515 default_global_perms = self.sa.query(UserToPerm)\
516 516 .filter(UserToPerm.user_id == default_user_id)
517 517
518 518 for perm in default_global_perms:
519 519 user.permissions[GLOBAL].add(perm.permission.permission_name)
520 520
521 521 # defaults for repositories, taken from default user
522 522 for perm in default_repo_perms:
523 523 r_k = perm.UserRepoToPerm.repository.repo_name
524 524 if perm.Repository.private and not (perm.Repository.user_id == uid):
525 525 # disable defaults for private repos,
526 526 p = 'repository.none'
527 527 elif perm.Repository.user_id == uid:
528 528 # set admin if owner
529 529 p = 'repository.admin'
530 530 else:
531 531 p = perm.Permission.permission_name
532 532
533 533 user.permissions[RK][r_k] = p
534 534
535 535 # defaults for repository groups taken from default user permission
536 536 # on given group
537 537 for perm in default_repo_groups_perms:
538 538 rg_k = perm.UserRepoGroupToPerm.group.group_name
539 539 p = perm.Permission.permission_name
540 540 user.permissions[GK][rg_k] = p
541 541
542 542 # defaults for user groups taken from default user permission
543 543 # on given user group
544 544 for perm in default_user_group_perms:
545 545 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
546 546 p = perm.Permission.permission_name
547 547 user.permissions[UK][u_k] = p
548 548
549 549 #======================================================================
550 550 # !! OVERRIDE GLOBALS !! with user permissions if any found
551 551 #======================================================================
552 552 # those can be configured from groups or users explicitly
553 553 _configurable = set([
554 554 'hg.fork.none', 'hg.fork.repository',
555 555 'hg.create.none', 'hg.create.repository',
556 556 'hg.usergroup.create.false', 'hg.usergroup.create.true'
557 557 ])
558 558
559 559 # USER GROUPS comes first
560 560 # user group global permissions
561 561 user_perms_from_users_groups = self.sa.query(UserGroupToPerm)\
562 562 .options(joinedload(UserGroupToPerm.permission))\
563 563 .join((UserGroupMember, UserGroupToPerm.users_group_id ==
564 564 UserGroupMember.users_group_id))\
565 565 .filter(UserGroupMember.user_id == uid)\
566 566 .order_by(UserGroupToPerm.users_group_id)\
567 567 .all()
568 568 #need to group here by groups since user can be in more than one group
569 569 _grouped = [[x, list(y)] for x, y in
570 570 itertools.groupby(user_perms_from_users_groups,
571 571 lambda x:x.users_group)]
572 572 for gr, perms in _grouped:
573 573 # since user can be in multiple groups iterate over them and
574 574 # select the lowest permissions first (more explicit)
575 575 ##TODO: do this^^
576 576 if not gr.inherit_default_permissions:
577 577 # NEED TO IGNORE all configurable permissions and
578 578 # replace them with explicitly set
579 579 user.permissions[GLOBAL] = user.permissions[GLOBAL]\
580 580 .difference(_configurable)
581 581 for perm in perms:
582 582 user.permissions[GLOBAL].add(perm.permission.permission_name)
583 583
584 584 # user specific global permissions
585 585 user_perms = self.sa.query(UserToPerm)\
586 586 .options(joinedload(UserToPerm.permission))\
587 587 .filter(UserToPerm.user_id == uid).all()
588 588
589 589 if not user.inherit_default_permissions:
590 590 # NEED TO IGNORE all configurable permissions and
591 591 # replace them with explicitly set
592 592 user.permissions[GLOBAL] = user.permissions[GLOBAL]\
593 593 .difference(_configurable)
594 594
595 595 for perm in user_perms:
596 596 user.permissions[GLOBAL].add(perm.permission.permission_name)
597 597 ## END GLOBAL PERMISSIONS
598 598
599 599 #======================================================================
600 600 # !! PERMISSIONS FOR REPOSITORIES !!
601 601 #======================================================================
602 602 #======================================================================
603 603 # check if user is part of user groups for this repository and
604 604 # fill in his permission from it. _choose_perm decides of which
605 605 # permission should be selected based on selected method
606 606 #======================================================================
607 607
608 608 # user group for repositories permissions
609 609 user_repo_perms_from_users_groups = \
610 610 self.sa.query(UserGroupRepoToPerm, Permission, Repository,)\
611 611 .join((Repository, UserGroupRepoToPerm.repository_id ==
612 612 Repository.repo_id))\
613 613 .join((Permission, UserGroupRepoToPerm.permission_id ==
614 614 Permission.permission_id))\
615 615 .join((UserGroupMember, UserGroupRepoToPerm.users_group_id ==
616 616 UserGroupMember.users_group_id))\
617 617 .filter(UserGroupMember.user_id == uid)\
618 618 .all()
619 619
620 620 multiple_counter = collections.defaultdict(int)
621 621 for perm in user_repo_perms_from_users_groups:
622 622 r_k = perm.UserGroupRepoToPerm.repository.repo_name
623 623 multiple_counter[r_k] += 1
624 624 p = perm.Permission.permission_name
625 625 cur_perm = user.permissions[RK][r_k]
626 626
627 627 if perm.Repository.user_id == uid:
628 628 # set admin if owner
629 629 p = 'repository.admin'
630 630 else:
631 631 if multiple_counter[r_k] > 1:
632 632 p = _choose_perm(p, cur_perm)
633 633 user.permissions[RK][r_k] = p
634 634
635 635 # user explicit permissions for repositories, overrides any specified
636 636 # by the group permission
637 637 user_repo_perms = Permission.get_default_perms(uid)
638 638 for perm in user_repo_perms:
639 639 r_k = perm.UserRepoToPerm.repository.repo_name
640 640 cur_perm = user.permissions[RK][r_k]
641 641 # set admin if owner
642 642 if perm.Repository.user_id == uid:
643 643 p = 'repository.admin'
644 644 else:
645 645 p = perm.Permission.permission_name
646 646 if not explicit:
647 647 p = _choose_perm(p, cur_perm)
648 648 user.permissions[RK][r_k] = p
649 649
650 650 #======================================================================
651 651 # !! PERMISSIONS FOR REPOSITORY GROUPS !!
652 652 #======================================================================
653 653 #======================================================================
654 654 # check if user is part of user groups for this repository groups and
655 655 # fill in his permission from it. _choose_perm decides of which
656 656 # permission should be selected based on selected method
657 657 #======================================================================
658 658 # user group for repo groups permissions
659 659 user_repo_group_perms_from_users_groups = \
660 660 self.sa.query(UserGroupRepoGroupToPerm, Permission, RepoGroup)\
661 661 .join((RepoGroup, UserGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
662 662 .join((Permission, UserGroupRepoGroupToPerm.permission_id
663 663 == Permission.permission_id))\
664 664 .join((UserGroupMember, UserGroupRepoGroupToPerm.users_group_id
665 665 == UserGroupMember.users_group_id))\
666 666 .filter(UserGroupMember.user_id == uid)\
667 667 .all()
668 668
669 669 multiple_counter = collections.defaultdict(int)
670 670 for perm in user_repo_group_perms_from_users_groups:
671 671 g_k = perm.UserGroupRepoGroupToPerm.group.group_name
672 672 multiple_counter[g_k] += 1
673 673 p = perm.Permission.permission_name
674 674 cur_perm = user.permissions[GK][g_k]
675 675 if multiple_counter[g_k] > 1:
676 676 p = _choose_perm(p, cur_perm)
677 677 user.permissions[GK][g_k] = p
678 678
679 679 # user explicit permissions for repository groups
680 680 user_repo_groups_perms = Permission.get_default_group_perms(uid)
681 681 for perm in user_repo_groups_perms:
682 682 rg_k = perm.UserRepoGroupToPerm.group.group_name
683 683 p = perm.Permission.permission_name
684 684 cur_perm = user.permissions[GK][rg_k]
685 685 if not explicit:
686 686 p = _choose_perm(p, cur_perm)
687 687 user.permissions[GK][rg_k] = p
688 688
689 689 #======================================================================
690 690 # !! PERMISSIONS FOR USER GROUPS !!
691 691 #======================================================================
692 692 # user group for user group permissions
693 693 user_group_user_groups_perms = \
694 694 self.sa.query(UserGroupUserGroupToPerm, Permission, UserGroup)\
695 695 .join((UserGroup, UserGroupUserGroupToPerm.target_user_group_id
696 696 == UserGroup.users_group_id))\
697 697 .join((Permission, UserGroupUserGroupToPerm.permission_id
698 698 == Permission.permission_id))\
699 699 .join((UserGroupMember, UserGroupUserGroupToPerm.user_group_id
700 700 == UserGroupMember.users_group_id))\
701 701 .filter(UserGroupMember.user_id == uid)\
702 702 .all()
703 703
704 704 multiple_counter = collections.defaultdict(int)
705 705 for perm in user_group_user_groups_perms:
706 706 g_k = perm.UserGroupUserGroupToPerm.target_user_group.users_group_name
707 707 multiple_counter[g_k] += 1
708 708 p = perm.Permission.permission_name
709 709 cur_perm = user.permissions[UK][g_k]
710 710 if multiple_counter[g_k] > 1:
711 711 p = _choose_perm(p, cur_perm)
712 712 user.permissions[UK][g_k] = p
713 713
714 714 #user explicit permission for user groups
715 715 user_user_groups_perms = Permission.get_default_user_group_perms(uid)
716 716 for perm in user_user_groups_perms:
717 717 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
718 718 p = perm.Permission.permission_name
719 719 cur_perm = user.permissions[UK][u_k]
720 720 if not explicit:
721 721 p = _choose_perm(p, cur_perm)
722 722 user.permissions[UK][u_k] = p
723 723
724 724 return user
725 725
726 726 def has_perm(self, user, perm):
727 727 perm = self._get_perm(perm)
728 728 user = self._get_user(user)
729 729
730 730 return UserToPerm.query().filter(UserToPerm.user == user)\
731 731 .filter(UserToPerm.permission == perm).scalar() is not None
732 732
733 733 def grant_perm(self, user, perm):
734 734 """
735 735 Grant user global permissions
736 736
737 737 :param user:
738 738 :param perm:
739 739 """
740 740 user = self._get_user(user)
741 741 perm = self._get_perm(perm)
742 742 # if this permission is already granted skip it
743 743 _perm = UserToPerm.query()\
744 744 .filter(UserToPerm.user == user)\
745 745 .filter(UserToPerm.permission == perm)\
746 746 .scalar()
747 747 if _perm:
748 748 return
749 749 new = UserToPerm()
750 750 new.user = user
751 751 new.permission = perm
752 752 self.sa.add(new)
753 753
754 754 def revoke_perm(self, user, perm):
755 755 """
756 756 Revoke users global permissions
757 757
758 758 :param user:
759 759 :param perm:
760 760 """
761 761 user = self._get_user(user)
762 762 perm = self._get_perm(perm)
763 763
764 764 obj = UserToPerm.query()\
765 765 .filter(UserToPerm.user == user)\
766 766 .filter(UserToPerm.permission == perm)\
767 767 .scalar()
768 768 if obj:
769 769 self.sa.delete(obj)
770 770
771 771 def add_extra_email(self, user, email):
772 772 """
773 773 Adds email address to UserEmailMap
774 774
775 775 :param user:
776 776 :param email:
777 777 """
778 778 from rhodecode.model import forms
779 779 form = forms.UserExtraEmailForm()()
780 780 data = form.to_python(dict(email=email))
781 781 user = self._get_user(user)
782 782
783 783 obj = UserEmailMap()
784 784 obj.user = user
785 785 obj.email = data['email']
786 786 self.sa.add(obj)
787 787 return obj
788 788
789 789 def delete_extra_email(self, user, email_id):
790 790 """
791 791 Removes email address from UserEmailMap
792 792
793 793 :param user:
794 794 :param email_id:
795 795 """
796 796 user = self._get_user(user)
797 797 obj = UserEmailMap.query().get(email_id)
798 798 if obj:
799 799 self.sa.delete(obj)
800 800
801 801 def add_extra_ip(self, user, ip):
802 802 """
803 803 Adds ip address to UserIpMap
804 804
805 805 :param user:
806 806 :param ip:
807 807 """
808 808 from rhodecode.model import forms
809 809 form = forms.UserExtraIpForm()()
810 810 data = form.to_python(dict(ip=ip))
811 811 user = self._get_user(user)
812 812
813 813 obj = UserIpMap()
814 814 obj.user = user
815 815 obj.ip_addr = data['ip']
816 816 self.sa.add(obj)
817 817 return obj
818 818
819 819 def delete_extra_ip(self, user, ip_id):
820 820 """
821 821 Removes ip address from UserIpMap
822 822
823 823 :param user:
824 824 :param ip_id:
825 825 """
826 826 user = self._get_user(user)
827 827 obj = UserIpMap.query().get(ip_id)
828 828 if obj:
829 829 self.sa.delete(obj)
General Comments 0
You need to be logged in to leave comments. Login now