##// END OF EJS Templates
Added email-map for alternative email addresses for users
marcink -
r2329:20e307d5 codereview
parent child Browse files
Show More
@@ -1,1352 +1,1400 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.db
4 4 ~~~~~~~~~~~~~~~~~~
5 5
6 6 Database Models for RhodeCode
7 7
8 8 :created_on: Apr 08, 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 os
27 27 import logging
28 28 import datetime
29 29 import traceback
30 30 from collections import defaultdict
31 31
32 32 from sqlalchemy import *
33 33 from sqlalchemy.ext.hybrid import hybrid_property
34 34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 35 from beaker.cache import cache_region, region_invalidate
36 36
37 37 from pylons.i18n.translation import lazy_ugettext as _
38 38
39 39 from rhodecode.lib.vcs import get_backend
40 40 from rhodecode.lib.vcs.utils.helpers import get_scm
41 41 from rhodecode.lib.vcs.exceptions import VCSError
42 42 from rhodecode.lib.vcs.utils.lazy import LazyProperty
43 43
44 44 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
45 45 safe_unicode
46 46 from rhodecode.lib.compat import json
47 47 from rhodecode.lib.caching_query import FromCache
48 48
49 49 from rhodecode.model.meta import Base, Session
50 50 import hashlib
51 from sqlalchemy.exc import DatabaseError
51 52
52 53
53 54 log = logging.getLogger(__name__)
54 55
55 56 #==============================================================================
56 57 # BASE CLASSES
57 58 #==============================================================================
58 59
59 60 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
60 61
61 62
62 63 class ModelSerializer(json.JSONEncoder):
63 64 """
64 65 Simple Serializer for JSON,
65 66
66 67 usage::
67 68
68 69 to make object customized for serialization implement a __json__
69 70 method that will return a dict for serialization into json
70 71
71 72 example::
72 73
73 74 class Task(object):
74 75
75 76 def __init__(self, name, value):
76 77 self.name = name
77 78 self.value = value
78 79
79 80 def __json__(self):
80 81 return dict(name=self.name,
81 82 value=self.value)
82 83
83 84 """
84 85
85 86 def default(self, obj):
86 87
87 88 if hasattr(obj, '__json__'):
88 89 return obj.__json__()
89 90 else:
90 91 return json.JSONEncoder.default(self, obj)
91 92
92 93
93 94 class BaseModel(object):
94 95 """
95 96 Base Model for all classess
96 97 """
97 98
98 99 @classmethod
99 100 def _get_keys(cls):
100 101 """return column names for this model """
101 102 return class_mapper(cls).c.keys()
102 103
103 104 def get_dict(self):
104 105 """
105 106 return dict with keys and values corresponding
106 107 to this model data """
107 108
108 109 d = {}
109 110 for k in self._get_keys():
110 111 d[k] = getattr(self, k)
111 112
112 113 # also use __json__() if present to get additional fields
113 114 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
114 115 d[k] = val
115 116 return d
116 117
117 118 def get_appstruct(self):
118 119 """return list with keys and values tupples corresponding
119 120 to this model data """
120 121
121 122 l = []
122 123 for k in self._get_keys():
123 124 l.append((k, getattr(self, k),))
124 125 return l
125 126
126 127 def populate_obj(self, populate_dict):
127 128 """populate model with data from given populate_dict"""
128 129
129 130 for k in self._get_keys():
130 131 if k in populate_dict:
131 132 setattr(self, k, populate_dict[k])
132 133
133 134 @classmethod
134 135 def query(cls):
135 136 return Session.query(cls)
136 137
137 138 @classmethod
138 139 def get(cls, id_):
139 140 if id_:
140 141 return cls.query().get(id_)
141 142
142 143 @classmethod
143 144 def getAll(cls):
144 145 return cls.query().all()
145 146
146 147 @classmethod
147 148 def delete(cls, id_):
148 149 obj = cls.query().get(id_)
149 150 Session.delete(obj)
150 151
151 152 def __repr__(self):
152 153 if hasattr(self, '__unicode__'):
153 154 # python repr needs to return str
154 155 return safe_str(self.__unicode__())
155 156 return '<DB:%s>' % (self.__class__.__name__)
156 157
157 158 class RhodeCodeSetting(Base, BaseModel):
158 159 __tablename__ = 'rhodecode_settings'
159 160 __table_args__ = (
160 161 UniqueConstraint('app_settings_name'),
161 162 {'extend_existing': True, 'mysql_engine': 'InnoDB',
162 163 'mysql_charset': 'utf8'}
163 164 )
164 165 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
165 166 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
166 167 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
167 168
168 169 def __init__(self, k='', v=''):
169 170 self.app_settings_name = k
170 171 self.app_settings_value = v
171 172
172 173 @validates('_app_settings_value')
173 174 def validate_settings_value(self, key, val):
174 175 assert type(val) == unicode
175 176 return val
176 177
177 178 @hybrid_property
178 179 def app_settings_value(self):
179 180 v = self._app_settings_value
180 181 if self.app_settings_name == 'ldap_active':
181 182 v = str2bool(v)
182 183 return v
183 184
184 185 @app_settings_value.setter
185 186 def app_settings_value(self, val):
186 187 """
187 188 Setter that will always make sure we use unicode in app_settings_value
188 189
189 190 :param val:
190 191 """
191 192 self._app_settings_value = safe_unicode(val)
192 193
193 194 def __unicode__(self):
194 195 return u"<%s('%s:%s')>" % (
195 196 self.__class__.__name__,
196 197 self.app_settings_name, self.app_settings_value
197 198 )
198 199
199 200 @classmethod
200 201 def get_by_name(cls, ldap_key):
201 202 return cls.query()\
202 203 .filter(cls.app_settings_name == ldap_key).scalar()
203 204
204 205 @classmethod
205 206 def get_app_settings(cls, cache=False):
206 207
207 208 ret = cls.query()
208 209
209 210 if cache:
210 211 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
211 212
212 213 if not ret:
213 214 raise Exception('Could not get application settings !')
214 215 settings = {}
215 216 for each in ret:
216 217 settings['rhodecode_' + each.app_settings_name] = \
217 218 each.app_settings_value
218 219
219 220 return settings
220 221
221 222 @classmethod
222 223 def get_ldap_settings(cls, cache=False):
223 224 ret = cls.query()\
224 225 .filter(cls.app_settings_name.startswith('ldap_')).all()
225 226 fd = {}
226 227 for row in ret:
227 228 fd.update({row.app_settings_name:row.app_settings_value})
228 229
229 230 return fd
230 231
231 232
232 233 class RhodeCodeUi(Base, BaseModel):
233 234 __tablename__ = 'rhodecode_ui'
234 235 __table_args__ = (
235 236 UniqueConstraint('ui_key'),
236 237 {'extend_existing': True, 'mysql_engine': 'InnoDB',
237 238 'mysql_charset': 'utf8'}
238 239 )
239 240
240 241 HOOK_UPDATE = 'changegroup.update'
241 242 HOOK_REPO_SIZE = 'changegroup.repo_size'
242 243 HOOK_PUSH = 'pretxnchangegroup.push_logger'
243 244 HOOK_PULL = 'preoutgoing.pull_logger'
244 245
245 246 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
246 247 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
247 248 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
248 249 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
249 250 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
250 251
251 252 @classmethod
252 253 def get_by_key(cls, key):
253 254 return cls.query().filter(cls.ui_key == key)
254 255
255 256 @classmethod
256 257 def get_builtin_hooks(cls):
257 258 q = cls.query()
258 259 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
259 260 cls.HOOK_REPO_SIZE,
260 261 cls.HOOK_PUSH, cls.HOOK_PULL]))
261 262 return q.all()
262 263
263 264 @classmethod
264 265 def get_custom_hooks(cls):
265 266 q = cls.query()
266 267 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
267 268 cls.HOOK_REPO_SIZE,
268 269 cls.HOOK_PUSH, cls.HOOK_PULL]))
269 270 q = q.filter(cls.ui_section == 'hooks')
270 271 return q.all()
271 272
272 273 @classmethod
273 274 def create_or_update_hook(cls, key, val):
274 275 new_ui = cls.get_by_key(key).scalar() or cls()
275 276 new_ui.ui_section = 'hooks'
276 277 new_ui.ui_active = True
277 278 new_ui.ui_key = key
278 279 new_ui.ui_value = val
279 280
280 281 Session.add(new_ui)
281 282
282 283
283 284 class User(Base, BaseModel):
284 285 __tablename__ = 'users'
285 286 __table_args__ = (
286 287 UniqueConstraint('username'), UniqueConstraint('email'),
287 288 {'extend_existing': True, 'mysql_engine': 'InnoDB',
288 289 'mysql_charset': 'utf8'}
289 290 )
290 291 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
291 292 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
292 293 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
293 294 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
294 295 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
295 296 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
296 297 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
297 298 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
298 299 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
299 300 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
300 301 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
301 302
302 303 user_log = relationship('UserLog', cascade='all')
303 304 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
304 305
305 306 repositories = relationship('Repository')
306 307 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
307 308 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
308 309 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
309 310
310 311 group_member = relationship('UsersGroupMember', cascade='all')
311 312
312 313 notifications = relationship('UserNotification', cascade='all')
313 314 # notifications assigned to this user
314 315 user_created_notifications = relationship('Notification', cascade='all')
315 316 # comments created by this user
316 317 user_comments = relationship('ChangesetComment', cascade='all')
317 318
318 319 @hybrid_property
319 320 def email(self):
320 321 return self._email
321 322
322 323 @email.setter
323 324 def email(self, val):
324 325 self._email = val.lower() if val else None
325 326
326 327 @property
327 328 def full_name(self):
328 329 return '%s %s' % (self.name, self.lastname)
329 330
330 331 @property
331 332 def full_name_or_username(self):
332 333 return ('%s %s' % (self.name, self.lastname)
333 334 if (self.name and self.lastname) else self.username)
334 335
335 336 @property
336 337 def full_contact(self):
337 338 return '%s %s <%s>' % (self.name, self.lastname, self.email)
338 339
339 340 @property
340 341 def short_contact(self):
341 342 return '%s %s' % (self.name, self.lastname)
342 343
343 344 @property
344 345 def is_admin(self):
345 346 return self.admin
346 347
347 348 def __unicode__(self):
348 349 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
349 350 self.user_id, self.username)
350 351
351 352 @classmethod
352 353 def get_by_username(cls, username, case_insensitive=False, cache=False):
353 354 if case_insensitive:
354 355 q = cls.query().filter(cls.username.ilike(username))
355 356 else:
356 357 q = cls.query().filter(cls.username == username)
357 358
358 359 if cache:
359 360 q = q.options(FromCache(
360 361 "sql_cache_short",
361 362 "get_user_%s" % _hash_key(username)
362 363 )
363 364 )
364 365 return q.scalar()
365 366
366 367 @classmethod
367 368 def get_by_api_key(cls, api_key, cache=False):
368 369 q = cls.query().filter(cls.api_key == api_key)
369 370
370 371 if cache:
371 372 q = q.options(FromCache("sql_cache_short",
372 373 "get_api_key_%s" % api_key))
373 374 return q.scalar()
374 375
375 376 @classmethod
376 377 def get_by_email(cls, email, case_insensitive=False, cache=False):
377 378 if case_insensitive:
378 379 q = cls.query().filter(cls.email.ilike(email))
379 380 else:
380 381 q = cls.query().filter(cls.email == email)
381 382
382 383 if cache:
383 384 q = q.options(FromCache("sql_cache_short",
384 "get_api_key_%s" % email))
385 return q.scalar()
385 "get_email_key_%s" % email))
386
387 ret = q.scalar()
388 if ret is None:
389 q = UserEmailMap.query()
390 # try fetching in alternate email map
391 if case_insensitive:
392 q = q.filter(UserEmailMap.email.ilike(email))
393 else:
394 q = q.filter(UserEmailMap.email == email)
395 q = q.options(joinedload(UserEmailMap.user))
396 if cache:
397 q = q.options(FromCache("sql_cache_short",
398 "get_email_map_key_%s" % email))
399 ret = getattr(q.scalar(), 'user', None)
400
401 return ret
386 402
387 403 def update_lastlogin(self):
388 404 """Update user lastlogin"""
389 405 self.last_login = datetime.datetime.now()
390 406 Session.add(self)
391 407 log.debug('updated user %s lastlogin' % self.username)
392 408
393 409 def __json__(self):
394 410 return dict(
395 411 user_id=self.user_id,
396 412 first_name=self.name,
397 413 last_name=self.lastname,
398 414 email=self.email,
399 415 full_name=self.full_name,
400 416 full_name_or_username=self.full_name_or_username,
401 417 short_contact=self.short_contact,
402 418 full_contact=self.full_contact
403 419 )
404 420
405 421
422 class UserEmailMap(Base, BaseModel):
423 __tablename__ = 'user_email_map'
424 __table_args__ = (
425 UniqueConstraint('email'),
426 {'extend_existing': True, 'mysql_engine':'InnoDB',
427 'mysql_charset': 'utf8'}
428 )
429 __mapper_args__ = {}
430
431 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
432 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
433 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
434
435 user = relationship('User')
436
437 @validates('_email')
438 def validate_email(self, key, email):
439 # check if this email is not main one
440 main_email = Session.query(User).filter(User.email == email).scalar()
441 if main_email is not None:
442 raise AttributeError('email %s is present is user table' % email)
443 return email
444
445 @hybrid_property
446 def email(self):
447 return self._email
448
449 @email.setter
450 def email(self, val):
451 self._email = val.lower() if val else None
452
453
406 454 class UserLog(Base, BaseModel):
407 455 __tablename__ = 'user_logs'
408 456 __table_args__ = (
409 457 {'extend_existing': True, 'mysql_engine': 'InnoDB',
410 458 'mysql_charset': 'utf8'},
411 459 )
412 460 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
413 461 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
414 462 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
415 463 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
416 464 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
417 465 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
418 466 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
419 467
420 468 @property
421 469 def action_as_day(self):
422 470 return datetime.date(*self.action_date.timetuple()[:3])
423 471
424 472 user = relationship('User')
425 473 repository = relationship('Repository', cascade='')
426 474
427 475
428 476 class UsersGroup(Base, BaseModel):
429 477 __tablename__ = 'users_groups'
430 478 __table_args__ = (
431 479 {'extend_existing': True, 'mysql_engine': 'InnoDB',
432 480 'mysql_charset': 'utf8'},
433 481 )
434 482
435 483 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
436 484 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
437 485 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
438 486
439 487 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
440 488 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
441 489 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
442 490
443 491 def __unicode__(self):
444 492 return u'<userGroup(%s)>' % (self.users_group_name)
445 493
446 494 @classmethod
447 495 def get_by_group_name(cls, group_name, cache=False,
448 496 case_insensitive=False):
449 497 if case_insensitive:
450 498 q = cls.query().filter(cls.users_group_name.ilike(group_name))
451 499 else:
452 500 q = cls.query().filter(cls.users_group_name == group_name)
453 501 if cache:
454 502 q = q.options(FromCache(
455 503 "sql_cache_short",
456 504 "get_user_%s" % _hash_key(group_name)
457 505 )
458 506 )
459 507 return q.scalar()
460 508
461 509 @classmethod
462 510 def get(cls, users_group_id, cache=False):
463 511 users_group = cls.query()
464 512 if cache:
465 513 users_group = users_group.options(FromCache("sql_cache_short",
466 514 "get_users_group_%s" % users_group_id))
467 515 return users_group.get(users_group_id)
468 516
469 517
470 518 class UsersGroupMember(Base, BaseModel):
471 519 __tablename__ = 'users_groups_members'
472 520 __table_args__ = (
473 521 {'extend_existing': True, 'mysql_engine': 'InnoDB',
474 522 'mysql_charset': 'utf8'},
475 523 )
476 524
477 525 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
478 526 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
479 527 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
480 528
481 529 user = relationship('User', lazy='joined')
482 530 users_group = relationship('UsersGroup')
483 531
484 532 def __init__(self, gr_id='', u_id=''):
485 533 self.users_group_id = gr_id
486 534 self.user_id = u_id
487 535
488 536
489 537 class Repository(Base, BaseModel):
490 538 __tablename__ = 'repositories'
491 539 __table_args__ = (
492 540 UniqueConstraint('repo_name'),
493 541 {'extend_existing': True, 'mysql_engine': 'InnoDB',
494 542 'mysql_charset': 'utf8'},
495 543 )
496 544
497 545 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
498 546 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
499 547 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
500 548 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
501 549 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
502 550 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
503 551 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
504 552 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
505 553 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
506 554 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
507 555
508 556 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
509 557 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
510 558
511 559 user = relationship('User')
512 560 fork = relationship('Repository', remote_side=repo_id)
513 561 group = relationship('RepoGroup')
514 562 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
515 563 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
516 564 stats = relationship('Statistics', cascade='all', uselist=False)
517 565
518 566 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
519 567
520 568 logs = relationship('UserLog')
521 569
522 570 def __unicode__(self):
523 571 return u"<%s('%s:%s')>" % (self.__class__.__name__,self.repo_id,
524 572 self.repo_name)
525 573
526 574 @classmethod
527 575 def url_sep(cls):
528 576 return '/'
529 577
530 578 @classmethod
531 579 def get_by_repo_name(cls, repo_name):
532 580 q = Session.query(cls).filter(cls.repo_name == repo_name)
533 581 q = q.options(joinedload(Repository.fork))\
534 582 .options(joinedload(Repository.user))\
535 583 .options(joinedload(Repository.group))
536 584 return q.scalar()
537 585
538 586 @classmethod
539 587 def get_repo_forks(cls, repo_id):
540 588 return cls.query().filter(Repository.fork_id == repo_id)
541 589
542 590 @classmethod
543 591 def base_path(cls):
544 592 """
545 593 Returns base path when all repos are stored
546 594
547 595 :param cls:
548 596 """
549 597 q = Session.query(RhodeCodeUi)\
550 598 .filter(RhodeCodeUi.ui_key == cls.url_sep())
551 599 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
552 600 return q.one().ui_value
553 601
554 602 @property
555 603 def just_name(self):
556 604 return self.repo_name.split(Repository.url_sep())[-1]
557 605
558 606 @property
559 607 def groups_with_parents(self):
560 608 groups = []
561 609 if self.group is None:
562 610 return groups
563 611
564 612 cur_gr = self.group
565 613 groups.insert(0, cur_gr)
566 614 while 1:
567 615 gr = getattr(cur_gr, 'parent_group', None)
568 616 cur_gr = cur_gr.parent_group
569 617 if gr is None:
570 618 break
571 619 groups.insert(0, gr)
572 620
573 621 return groups
574 622
575 623 @property
576 624 def groups_and_repo(self):
577 625 return self.groups_with_parents, self.just_name
578 626
579 627 @LazyProperty
580 628 def repo_path(self):
581 629 """
582 630 Returns base full path for that repository means where it actually
583 631 exists on a filesystem
584 632 """
585 633 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
586 634 Repository.url_sep())
587 635 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
588 636 return q.one().ui_value
589 637
590 638 @property
591 639 def repo_full_path(self):
592 640 p = [self.repo_path]
593 641 # we need to split the name by / since this is how we store the
594 642 # names in the database, but that eventually needs to be converted
595 643 # into a valid system path
596 644 p += self.repo_name.split(Repository.url_sep())
597 645 return os.path.join(*p)
598 646
599 647 def get_new_name(self, repo_name):
600 648 """
601 649 returns new full repository name based on assigned group and new new
602 650
603 651 :param group_name:
604 652 """
605 653 path_prefix = self.group.full_path_splitted if self.group else []
606 654 return Repository.url_sep().join(path_prefix + [repo_name])
607 655
608 656 @property
609 657 def _ui(self):
610 658 """
611 659 Creates an db based ui object for this repository
612 660 """
613 661 from mercurial import ui
614 662 from mercurial import config
615 663 baseui = ui.ui()
616 664
617 665 #clean the baseui object
618 666 baseui._ocfg = config.config()
619 667 baseui._ucfg = config.config()
620 668 baseui._tcfg = config.config()
621 669
622 670 ret = RhodeCodeUi.query()\
623 671 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
624 672
625 673 hg_ui = ret
626 674 for ui_ in hg_ui:
627 675 if ui_.ui_active:
628 676 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
629 677 ui_.ui_key, ui_.ui_value)
630 678 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
631 679
632 680 return baseui
633 681
634 682 @classmethod
635 683 def is_valid(cls, repo_name):
636 684 """
637 685 returns True if given repo name is a valid filesystem repository
638 686
639 687 :param cls:
640 688 :param repo_name:
641 689 """
642 690 from rhodecode.lib.utils import is_valid_repo
643 691
644 692 return is_valid_repo(repo_name, cls.base_path())
645 693
646 694 #==========================================================================
647 695 # SCM PROPERTIES
648 696 #==========================================================================
649 697
650 698 def get_changeset(self, rev=None):
651 699 return get_changeset_safe(self.scm_instance, rev)
652 700
653 701 @property
654 702 def tip(self):
655 703 return self.get_changeset('tip')
656 704
657 705 @property
658 706 def author(self):
659 707 return self.tip.author
660 708
661 709 @property
662 710 def last_change(self):
663 711 return self.scm_instance.last_change
664 712
665 713 def comments(self, revisions=None):
666 714 """
667 715 Returns comments for this repository grouped by revisions
668 716
669 717 :param revisions: filter query by revisions only
670 718 """
671 719 cmts = ChangesetComment.query()\
672 720 .filter(ChangesetComment.repo == self)
673 721 if revisions:
674 722 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
675 723 grouped = defaultdict(list)
676 724 for cmt in cmts.all():
677 725 grouped[cmt.revision].append(cmt)
678 726 return grouped
679 727
680 728 def statuses(self, revisions=None):
681 729 """
682 730 Returns statuses for this repository
683 731
684 732 :param revisions: list of revisions to get statuses for
685 733 :type revisions: list
686 734 """
687 735
688 736 statuses = ChangesetStatus.query()\
689 737 .filter(ChangesetStatus.repo == self)\
690 738 .filter(ChangesetStatus.version == 0)
691 739 if revisions:
692 740 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
693 741 grouped = {}
694 742 for stat in statuses.all():
695 743 grouped[stat.revision] = [str(stat.status), stat.status_lbl]
696 744 return grouped
697 745
698 746 #==========================================================================
699 747 # SCM CACHE INSTANCE
700 748 #==========================================================================
701 749
702 750 @property
703 751 def invalidate(self):
704 752 return CacheInvalidation.invalidate(self.repo_name)
705 753
706 754 def set_invalidate(self):
707 755 """
708 756 set a cache for invalidation for this instance
709 757 """
710 758 CacheInvalidation.set_invalidate(self.repo_name)
711 759
712 760 @LazyProperty
713 761 def scm_instance(self):
714 762 return self.__get_instance()
715 763
716 764 @property
717 765 def scm_instance_cached(self):
718 766 @cache_region('long_term')
719 767 def _c(repo_name):
720 768 return self.__get_instance()
721 769 rn = self.repo_name
722 770 log.debug('Getting cached instance of repo')
723 771 inv = self.invalidate
724 772 if inv is not None:
725 773 region_invalidate(_c, None, rn)
726 774 # update our cache
727 775 CacheInvalidation.set_valid(inv.cache_key)
728 776 return _c(rn)
729 777
730 778 def __get_instance(self):
731 779 repo_full_path = self.repo_full_path
732 780 try:
733 781 alias = get_scm(repo_full_path)[0]
734 782 log.debug('Creating instance of %s repository' % alias)
735 783 backend = get_backend(alias)
736 784 except VCSError:
737 785 log.error(traceback.format_exc())
738 786 log.error('Perhaps this repository is in db and not in '
739 787 'filesystem run rescan repositories with '
740 788 '"destroy old data " option from admin panel')
741 789 return
742 790
743 791 if alias == 'hg':
744 792
745 793 repo = backend(safe_str(repo_full_path), create=False,
746 794 baseui=self._ui)
747 795 # skip hidden web repository
748 796 if repo._get_hidden():
749 797 return
750 798 else:
751 799 repo = backend(repo_full_path, create=False)
752 800
753 801 return repo
754 802
755 803
756 804 class RepoGroup(Base, BaseModel):
757 805 __tablename__ = 'groups'
758 806 __table_args__ = (
759 807 UniqueConstraint('group_name', 'group_parent_id'),
760 808 CheckConstraint('group_id != group_parent_id'),
761 809 {'extend_existing': True, 'mysql_engine': 'InnoDB',
762 810 'mysql_charset': 'utf8'},
763 811 )
764 812 __mapper_args__ = {'order_by': 'group_name'}
765 813
766 814 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
767 815 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
768 816 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
769 817 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
770 818
771 819 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
772 820 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
773 821
774 822 parent_group = relationship('RepoGroup', remote_side=group_id)
775 823
776 824 def __init__(self, group_name='', parent_group=None):
777 825 self.group_name = group_name
778 826 self.parent_group = parent_group
779 827
780 828 def __unicode__(self):
781 829 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
782 830 self.group_name)
783 831
784 832 @classmethod
785 833 def groups_choices(cls):
786 834 from webhelpers.html import literal as _literal
787 835 repo_groups = [('', '')]
788 836 sep = ' &raquo; '
789 837 _name = lambda k: _literal(sep.join(k))
790 838
791 839 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
792 840 for x in cls.query().all()])
793 841
794 842 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
795 843 return repo_groups
796 844
797 845 @classmethod
798 846 def url_sep(cls):
799 847 return '/'
800 848
801 849 @classmethod
802 850 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
803 851 if case_insensitive:
804 852 gr = cls.query()\
805 853 .filter(cls.group_name.ilike(group_name))
806 854 else:
807 855 gr = cls.query()\
808 856 .filter(cls.group_name == group_name)
809 857 if cache:
810 858 gr = gr.options(FromCache(
811 859 "sql_cache_short",
812 860 "get_group_%s" % _hash_key(group_name)
813 861 )
814 862 )
815 863 return gr.scalar()
816 864
817 865 @property
818 866 def parents(self):
819 867 parents_recursion_limit = 5
820 868 groups = []
821 869 if self.parent_group is None:
822 870 return groups
823 871 cur_gr = self.parent_group
824 872 groups.insert(0, cur_gr)
825 873 cnt = 0
826 874 while 1:
827 875 cnt += 1
828 876 gr = getattr(cur_gr, 'parent_group', None)
829 877 cur_gr = cur_gr.parent_group
830 878 if gr is None:
831 879 break
832 880 if cnt == parents_recursion_limit:
833 881 # this will prevent accidental infinit loops
834 882 log.error('group nested more than %s' %
835 883 parents_recursion_limit)
836 884 break
837 885
838 886 groups.insert(0, gr)
839 887 return groups
840 888
841 889 @property
842 890 def children(self):
843 891 return RepoGroup.query().filter(RepoGroup.parent_group == self)
844 892
845 893 @property
846 894 def name(self):
847 895 return self.group_name.split(RepoGroup.url_sep())[-1]
848 896
849 897 @property
850 898 def full_path(self):
851 899 return self.group_name
852 900
853 901 @property
854 902 def full_path_splitted(self):
855 903 return self.group_name.split(RepoGroup.url_sep())
856 904
857 905 @property
858 906 def repositories(self):
859 907 return Repository.query()\
860 908 .filter(Repository.group == self)\
861 909 .order_by(Repository.repo_name)
862 910
863 911 @property
864 912 def repositories_recursive_count(self):
865 913 cnt = self.repositories.count()
866 914
867 915 def children_count(group):
868 916 cnt = 0
869 917 for child in group.children:
870 918 cnt += child.repositories.count()
871 919 cnt += children_count(child)
872 920 return cnt
873 921
874 922 return cnt + children_count(self)
875 923
876 924 def get_new_name(self, group_name):
877 925 """
878 926 returns new full group name based on parent and new name
879 927
880 928 :param group_name:
881 929 """
882 930 path_prefix = (self.parent_group.full_path_splitted if
883 931 self.parent_group else [])
884 932 return RepoGroup.url_sep().join(path_prefix + [group_name])
885 933
886 934
887 935 class Permission(Base, BaseModel):
888 936 __tablename__ = 'permissions'
889 937 __table_args__ = (
890 938 {'extend_existing': True, 'mysql_engine': 'InnoDB',
891 939 'mysql_charset': 'utf8'},
892 940 )
893 941 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
894 942 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
895 943 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
896 944
897 945 def __unicode__(self):
898 946 return u"<%s('%s:%s')>" % (
899 947 self.__class__.__name__, self.permission_id, self.permission_name
900 948 )
901 949
902 950 @classmethod
903 951 def get_by_key(cls, key):
904 952 return cls.query().filter(cls.permission_name == key).scalar()
905 953
906 954 @classmethod
907 955 def get_default_perms(cls, default_user_id):
908 956 q = Session.query(UserRepoToPerm, Repository, cls)\
909 957 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
910 958 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
911 959 .filter(UserRepoToPerm.user_id == default_user_id)
912 960
913 961 return q.all()
914 962
915 963 @classmethod
916 964 def get_default_group_perms(cls, default_user_id):
917 965 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
918 966 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
919 967 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
920 968 .filter(UserRepoGroupToPerm.user_id == default_user_id)
921 969
922 970 return q.all()
923 971
924 972
925 973 class UserRepoToPerm(Base, BaseModel):
926 974 __tablename__ = 'repo_to_perm'
927 975 __table_args__ = (
928 976 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
929 977 {'extend_existing': True, 'mysql_engine': 'InnoDB',
930 978 'mysql_charset': 'utf8'}
931 979 )
932 980 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
933 981 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
934 982 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
935 983 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
936 984
937 985 user = relationship('User')
938 986 repository = relationship('Repository')
939 987 permission = relationship('Permission')
940 988
941 989 @classmethod
942 990 def create(cls, user, repository, permission):
943 991 n = cls()
944 992 n.user = user
945 993 n.repository = repository
946 994 n.permission = permission
947 995 Session.add(n)
948 996 return n
949 997
950 998 def __unicode__(self):
951 999 return u'<user:%s => %s >' % (self.user, self.repository)
952 1000
953 1001
954 1002 class UserToPerm(Base, BaseModel):
955 1003 __tablename__ = 'user_to_perm'
956 1004 __table_args__ = (
957 1005 UniqueConstraint('user_id', 'permission_id'),
958 1006 {'extend_existing': True, 'mysql_engine': 'InnoDB',
959 1007 'mysql_charset': 'utf8'}
960 1008 )
961 1009 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
962 1010 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
963 1011 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
964 1012
965 1013 user = relationship('User')
966 1014 permission = relationship('Permission', lazy='joined')
967 1015
968 1016
969 1017 class UsersGroupRepoToPerm(Base, BaseModel):
970 1018 __tablename__ = 'users_group_repo_to_perm'
971 1019 __table_args__ = (
972 1020 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
973 1021 {'extend_existing': True, 'mysql_engine': 'InnoDB',
974 1022 'mysql_charset': 'utf8'}
975 1023 )
976 1024 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
977 1025 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
978 1026 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
979 1027 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
980 1028
981 1029 users_group = relationship('UsersGroup')
982 1030 permission = relationship('Permission')
983 1031 repository = relationship('Repository')
984 1032
985 1033 @classmethod
986 1034 def create(cls, users_group, repository, permission):
987 1035 n = cls()
988 1036 n.users_group = users_group
989 1037 n.repository = repository
990 1038 n.permission = permission
991 1039 Session.add(n)
992 1040 return n
993 1041
994 1042 def __unicode__(self):
995 1043 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
996 1044
997 1045
998 1046 class UsersGroupToPerm(Base, BaseModel):
999 1047 __tablename__ = 'users_group_to_perm'
1000 1048 __table_args__ = (
1001 1049 UniqueConstraint('users_group_id', 'permission_id',),
1002 1050 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1003 1051 'mysql_charset': 'utf8'}
1004 1052 )
1005 1053 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1006 1054 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1007 1055 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1008 1056
1009 1057 users_group = relationship('UsersGroup')
1010 1058 permission = relationship('Permission')
1011 1059
1012 1060
1013 1061 class UserRepoGroupToPerm(Base, BaseModel):
1014 1062 __tablename__ = 'user_repo_group_to_perm'
1015 1063 __table_args__ = (
1016 1064 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1017 1065 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1018 1066 'mysql_charset': 'utf8'}
1019 1067 )
1020 1068
1021 1069 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1022 1070 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1023 1071 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1024 1072 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1025 1073
1026 1074 user = relationship('User')
1027 1075 group = relationship('RepoGroup')
1028 1076 permission = relationship('Permission')
1029 1077
1030 1078
1031 1079 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1032 1080 __tablename__ = 'users_group_repo_group_to_perm'
1033 1081 __table_args__ = (
1034 1082 UniqueConstraint('users_group_id', 'group_id'),
1035 1083 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1036 1084 'mysql_charset': 'utf8'}
1037 1085 )
1038 1086
1039 1087 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1040 1088 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1041 1089 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1042 1090 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1043 1091
1044 1092 users_group = relationship('UsersGroup')
1045 1093 permission = relationship('Permission')
1046 1094 group = relationship('RepoGroup')
1047 1095
1048 1096
1049 1097 class Statistics(Base, BaseModel):
1050 1098 __tablename__ = 'statistics'
1051 1099 __table_args__ = (
1052 1100 UniqueConstraint('repository_id'),
1053 1101 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1054 1102 'mysql_charset': 'utf8'}
1055 1103 )
1056 1104 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1057 1105 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1058 1106 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1059 1107 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1060 1108 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1061 1109 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1062 1110
1063 1111 repository = relationship('Repository', single_parent=True)
1064 1112
1065 1113
1066 1114 class UserFollowing(Base, BaseModel):
1067 1115 __tablename__ = 'user_followings'
1068 1116 __table_args__ = (
1069 1117 UniqueConstraint('user_id', 'follows_repository_id'),
1070 1118 UniqueConstraint('user_id', 'follows_user_id'),
1071 1119 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1072 1120 'mysql_charset': 'utf8'}
1073 1121 )
1074 1122
1075 1123 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1076 1124 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1077 1125 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1078 1126 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1079 1127 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1080 1128
1081 1129 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1082 1130
1083 1131 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1084 1132 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1085 1133
1086 1134 @classmethod
1087 1135 def get_repo_followers(cls, repo_id):
1088 1136 return cls.query().filter(cls.follows_repo_id == repo_id)
1089 1137
1090 1138
1091 1139 class CacheInvalidation(Base, BaseModel):
1092 1140 __tablename__ = 'cache_invalidation'
1093 1141 __table_args__ = (
1094 1142 UniqueConstraint('cache_key'),
1095 1143 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1096 1144 'mysql_charset': 'utf8'},
1097 1145 )
1098 1146 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1099 1147 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1100 1148 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1101 1149 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1102 1150
1103 1151 def __init__(self, cache_key, cache_args=''):
1104 1152 self.cache_key = cache_key
1105 1153 self.cache_args = cache_args
1106 1154 self.cache_active = False
1107 1155
1108 1156 def __unicode__(self):
1109 1157 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1110 1158 self.cache_id, self.cache_key)
1111 1159 @classmethod
1112 1160 def clear_cache(cls):
1113 1161 cls.query().delete()
1114 1162
1115 1163 @classmethod
1116 1164 def _get_key(cls, key):
1117 1165 """
1118 1166 Wrapper for generating a key, together with a prefix
1119 1167
1120 1168 :param key:
1121 1169 """
1122 1170 import rhodecode
1123 1171 prefix = ''
1124 1172 iid = rhodecode.CONFIG.get('instance_id')
1125 1173 if iid:
1126 1174 prefix = iid
1127 1175 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1128 1176
1129 1177 @classmethod
1130 1178 def get_by_key(cls, key):
1131 1179 return cls.query().filter(cls.cache_key == key).scalar()
1132 1180
1133 1181 @classmethod
1134 1182 def _get_or_create_key(cls, key, prefix, org_key):
1135 1183 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
1136 1184 if not inv_obj:
1137 1185 try:
1138 1186 inv_obj = CacheInvalidation(key, org_key)
1139 1187 Session.add(inv_obj)
1140 1188 Session.commit()
1141 1189 except Exception:
1142 1190 log.error(traceback.format_exc())
1143 1191 Session.rollback()
1144 1192 return inv_obj
1145 1193
1146 1194 @classmethod
1147 1195 def invalidate(cls, key):
1148 1196 """
1149 1197 Returns Invalidation object if this given key should be invalidated
1150 1198 None otherwise. `cache_active = False` means that this cache
1151 1199 state is not valid and needs to be invalidated
1152 1200
1153 1201 :param key:
1154 1202 """
1155 1203
1156 1204 key, _prefix, _org_key = cls._get_key(key)
1157 1205 inv = cls._get_or_create_key(key, _prefix, _org_key)
1158 1206
1159 1207 if inv and inv.cache_active is False:
1160 1208 return inv
1161 1209
1162 1210 @classmethod
1163 1211 def set_invalidate(cls, key):
1164 1212 """
1165 1213 Mark this Cache key for invalidation
1166 1214
1167 1215 :param key:
1168 1216 """
1169 1217
1170 1218 key, _prefix, _org_key = cls._get_key(key)
1171 1219 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
1172 1220 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1173 1221 _org_key))
1174 1222 try:
1175 1223 for inv_obj in inv_objs:
1176 1224 if inv_obj:
1177 1225 inv_obj.cache_active = False
1178 1226
1179 1227 Session.add(inv_obj)
1180 1228 Session.commit()
1181 1229 except Exception:
1182 1230 log.error(traceback.format_exc())
1183 1231 Session.rollback()
1184 1232
1185 1233 @classmethod
1186 1234 def set_valid(cls, key):
1187 1235 """
1188 1236 Mark this cache key as active and currently cached
1189 1237
1190 1238 :param key:
1191 1239 """
1192 1240 inv_obj = cls.get_by_key(key)
1193 1241 inv_obj.cache_active = True
1194 1242 Session.add(inv_obj)
1195 1243 Session.commit()
1196 1244
1197 1245
1198 1246 class ChangesetComment(Base, BaseModel):
1199 1247 __tablename__ = 'changeset_comments'
1200 1248 __table_args__ = (
1201 1249 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1202 1250 'mysql_charset': 'utf8'},
1203 1251 )
1204 1252 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1205 1253 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1206 1254 revision = Column('revision', String(40), nullable=False)
1207 1255 line_no = Column('line_no', Unicode(10), nullable=True)
1208 1256 f_path = Column('f_path', Unicode(1000), nullable=True)
1209 1257 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1210 1258 text = Column('text', Unicode(25000), nullable=False)
1211 1259 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1212 1260
1213 1261 author = relationship('User', lazy='joined')
1214 1262 repo = relationship('Repository')
1215 1263 status_change = relationship('ChangesetStatus', uselist=False)
1216 1264
1217 1265 @classmethod
1218 1266 def get_users(cls, revision):
1219 1267 """
1220 1268 Returns user associated with this changesetComment. ie those
1221 1269 who actually commented
1222 1270
1223 1271 :param cls:
1224 1272 :param revision:
1225 1273 """
1226 1274 return Session.query(User)\
1227 1275 .filter(cls.revision == revision)\
1228 1276 .join(ChangesetComment.author).all()
1229 1277
1230 1278
1231 1279 class ChangesetStatus(Base, BaseModel):
1232 1280 __tablename__ = 'changeset_statuses'
1233 1281 __table_args__ = (
1234 1282 UniqueConstraint('repo_id', 'revision', 'version'),
1235 1283 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1236 1284 'mysql_charset': 'utf8'}
1237 1285 )
1238 1286
1239 1287 STATUSES = [
1240 1288 ('not_reviewed', _("Not Reviewed")), # (no icon) and default
1241 1289 ('approved', _("Approved")),
1242 1290 ('rejected', _("Rejected")),
1243 1291 ('under_review', _("Under Review")),
1244 1292 ]
1245 1293 DEFAULT = STATUSES[0][0]
1246 1294
1247 1295 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1248 1296 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1249 1297 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1250 1298 revision = Column('revision', String(40), nullable=False)
1251 1299 status = Column('status', String(128), nullable=False, default=DEFAULT)
1252 1300 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1253 1301 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1254 1302 version = Column('version', Integer(), nullable=False, default=0)
1255 1303 author = relationship('User', lazy='joined')
1256 1304 repo = relationship('Repository')
1257 1305 comment = relationship('ChangesetComment', lazy='joined')
1258 1306
1259 1307 @classmethod
1260 1308 def get_status_lbl(cls, value):
1261 1309 return dict(cls.STATUSES).get(value)
1262 1310
1263 1311 @property
1264 1312 def status_lbl(self):
1265 1313 return ChangesetStatus.get_status_lbl(self.status)
1266 1314
1267 1315
1268 1316 class Notification(Base, BaseModel):
1269 1317 __tablename__ = 'notifications'
1270 1318 __table_args__ = (
1271 1319 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1272 1320 'mysql_charset': 'utf8'},
1273 1321 )
1274 1322
1275 1323 TYPE_CHANGESET_COMMENT = u'cs_comment'
1276 1324 TYPE_MESSAGE = u'message'
1277 1325 TYPE_MENTION = u'mention'
1278 1326 TYPE_REGISTRATION = u'registration'
1279 1327 TYPE_PULL_REQUEST = u'pull_request'
1280 1328
1281 1329 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1282 1330 subject = Column('subject', Unicode(512), nullable=True)
1283 1331 body = Column('body', Unicode(50000), nullable=True)
1284 1332 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1285 1333 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1286 1334 type_ = Column('type', Unicode(256))
1287 1335
1288 1336 created_by_user = relationship('User')
1289 1337 notifications_to_users = relationship('UserNotification', lazy='joined',
1290 1338 cascade="all, delete, delete-orphan")
1291 1339
1292 1340 @property
1293 1341 def recipients(self):
1294 1342 return [x.user for x in UserNotification.query()\
1295 1343 .filter(UserNotification.notification == self)\
1296 1344 .order_by(UserNotification.user).all()]
1297 1345
1298 1346 @classmethod
1299 1347 def create(cls, created_by, subject, body, recipients, type_=None):
1300 1348 if type_ is None:
1301 1349 type_ = Notification.TYPE_MESSAGE
1302 1350
1303 1351 notification = cls()
1304 1352 notification.created_by_user = created_by
1305 1353 notification.subject = subject
1306 1354 notification.body = body
1307 1355 notification.type_ = type_
1308 1356 notification.created_on = datetime.datetime.now()
1309 1357
1310 1358 for u in recipients:
1311 1359 assoc = UserNotification()
1312 1360 assoc.notification = notification
1313 1361 u.notifications.append(assoc)
1314 1362 Session.add(notification)
1315 1363 return notification
1316 1364
1317 1365 @property
1318 1366 def description(self):
1319 1367 from rhodecode.model.notification import NotificationModel
1320 1368 return NotificationModel().make_description(self)
1321 1369
1322 1370
1323 1371 class UserNotification(Base, BaseModel):
1324 1372 __tablename__ = 'user_to_notification'
1325 1373 __table_args__ = (
1326 1374 UniqueConstraint('user_id', 'notification_id'),
1327 1375 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1328 1376 'mysql_charset': 'utf8'}
1329 1377 )
1330 1378 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1331 1379 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1332 1380 read = Column('read', Boolean, default=False)
1333 1381 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1334 1382
1335 1383 user = relationship('User', lazy="joined")
1336 1384 notification = relationship('Notification', lazy="joined",
1337 1385 order_by=lambda: Notification.created_on.desc(),)
1338 1386
1339 1387 def mark_as_read(self):
1340 1388 self.read = True
1341 1389 Session.add(self)
1342 1390
1343 1391
1344 1392 class DbMigrateVersion(Base, BaseModel):
1345 1393 __tablename__ = 'db_migrate_version'
1346 1394 __table_args__ = (
1347 1395 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1348 1396 'mysql_charset': 'utf8'},
1349 1397 )
1350 1398 repository_id = Column('repository_id', String(250), primary_key=True)
1351 1399 repository_path = Column('repository_path', Text)
1352 1400 version = Column('version', Integer)
@@ -1,715 +1,760 b''
1 1 import os
2 2 import unittest
3 3 from rhodecode.tests import *
4 4
5 5 from rhodecode.model.repos_group import ReposGroupModel
6 6 from rhodecode.model.repo import RepoModel
7 7 from rhodecode.model.db import RepoGroup, User, Notification, UserNotification, \
8 8 UsersGroup, UsersGroupMember, Permission, UsersGroupRepoGroupToPerm,\
9 Repository
10 from sqlalchemy.exc import IntegrityError
9 Repository, UserEmailMap
10 from sqlalchemy.exc import IntegrityError, DatabaseError
11 11 from rhodecode.model.user import UserModel
12 12
13 13 from rhodecode.model.meta import Session
14 14 from rhodecode.model.notification import NotificationModel
15 15 from rhodecode.model.users_group import UsersGroupModel
16 16 from rhodecode.lib.auth import AuthUser
17 17
18 18
19 19 def _make_group(path, desc='desc', parent_id=None,
20 20 skip_if_exists=False):
21 21
22 22 gr = RepoGroup.get_by_group_name(path)
23 23 if gr and skip_if_exists:
24 24 return gr
25 25
26 26 gr = ReposGroupModel().create(path, desc, parent_id)
27 27 return gr
28 28
29 29
30 30 class TestReposGroups(unittest.TestCase):
31 31
32 32 def setUp(self):
33 33 self.g1 = _make_group('test1', skip_if_exists=True)
34 34 Session.commit()
35 35 self.g2 = _make_group('test2', skip_if_exists=True)
36 36 Session.commit()
37 37 self.g3 = _make_group('test3', skip_if_exists=True)
38 38 Session.commit()
39 39
40 40 def tearDown(self):
41 41 print 'out'
42 42
43 43 def __check_path(self, *path):
44 44 """
45 45 Checks the path for existance !
46 46 """
47 47 path = [TESTS_TMP_PATH] + list(path)
48 48 path = os.path.join(*path)
49 49 return os.path.isdir(path)
50 50
51 51 def _check_folders(self):
52 52 print os.listdir(TESTS_TMP_PATH)
53 53
54 54 def __delete_group(self, id_):
55 55 ReposGroupModel().delete(id_)
56 56
57 57 def __update_group(self, id_, path, desc='desc', parent_id=None):
58 58 form_data = dict(
59 59 group_name=path,
60 60 group_description=desc,
61 61 group_parent_id=parent_id,
62 62 perms_updates=[],
63 63 perms_new=[]
64 64 )
65 65 gr = ReposGroupModel().update(id_, form_data)
66 66 return gr
67 67
68 68 def test_create_group(self):
69 69 g = _make_group('newGroup')
70 70 self.assertEqual(g.full_path, 'newGroup')
71 71
72 72 self.assertTrue(self.__check_path('newGroup'))
73 73
74 74 def test_create_same_name_group(self):
75 75 self.assertRaises(IntegrityError, lambda:_make_group('newGroup'))
76 76 Session.rollback()
77 77
78 78 def test_same_subgroup(self):
79 79 sg1 = _make_group('sub1', parent_id=self.g1.group_id)
80 80 self.assertEqual(sg1.parent_group, self.g1)
81 81 self.assertEqual(sg1.full_path, 'test1/sub1')
82 82 self.assertTrue(self.__check_path('test1', 'sub1'))
83 83
84 84 ssg1 = _make_group('subsub1', parent_id=sg1.group_id)
85 85 self.assertEqual(ssg1.parent_group, sg1)
86 86 self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
87 87 self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
88 88
89 89 def test_remove_group(self):
90 90 sg1 = _make_group('deleteme')
91 91 self.__delete_group(sg1.group_id)
92 92
93 93 self.assertEqual(RepoGroup.get(sg1.group_id), None)
94 94 self.assertFalse(self.__check_path('deteteme'))
95 95
96 96 sg1 = _make_group('deleteme', parent_id=self.g1.group_id)
97 97 self.__delete_group(sg1.group_id)
98 98
99 99 self.assertEqual(RepoGroup.get(sg1.group_id), None)
100 100 self.assertFalse(self.__check_path('test1', 'deteteme'))
101 101
102 102 def test_rename_single_group(self):
103 103 sg1 = _make_group('initial')
104 104
105 105 new_sg1 = self.__update_group(sg1.group_id, 'after')
106 106 self.assertTrue(self.__check_path('after'))
107 107 self.assertEqual(RepoGroup.get_by_group_name('initial'), None)
108 108
109 109 def test_update_group_parent(self):
110 110
111 111 sg1 = _make_group('initial', parent_id=self.g1.group_id)
112 112
113 113 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
114 114 self.assertTrue(self.__check_path('test1', 'after'))
115 115 self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None)
116 116
117 117 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
118 118 self.assertTrue(self.__check_path('test3', 'after'))
119 119 self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None)
120 120
121 121 new_sg1 = self.__update_group(sg1.group_id, 'hello')
122 122 self.assertTrue(self.__check_path('hello'))
123 123
124 124 self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1)
125 125
126 126 def test_subgrouping_with_repo(self):
127 127
128 128 g1 = _make_group('g1')
129 129 g2 = _make_group('g2')
130 130
131 131 # create new repo
132 132 form_data = dict(repo_name='john',
133 133 repo_name_full='john',
134 134 fork_name=None,
135 135 description=None,
136 136 repo_group=None,
137 137 private=False,
138 138 repo_type='hg',
139 139 clone_uri=None)
140 140 cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
141 141 r = RepoModel().create(form_data, cur_user)
142 142
143 143 self.assertEqual(r.repo_name, 'john')
144 144
145 145 # put repo into group
146 146 form_data = form_data
147 147 form_data['repo_group'] = g1.group_id
148 148 form_data['perms_new'] = []
149 149 form_data['perms_updates'] = []
150 150 RepoModel().update(r.repo_name, form_data)
151 151 self.assertEqual(r.repo_name, 'g1/john')
152 152
153 153 self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id)
154 154 self.assertTrue(self.__check_path('g2', 'g1'))
155 155
156 156 # test repo
157 157 self.assertEqual(r.repo_name, RepoGroup.url_sep().join(['g2', 'g1', r.just_name]))
158 158
159 159 def test_move_to_root(self):
160 160 g1 = _make_group('t11')
161 161 Session.commit()
162 162 g2 = _make_group('t22', parent_id=g1.group_id)
163 163 Session.commit()
164 164
165 165 self.assertEqual(g2.full_path, 't11/t22')
166 166 self.assertTrue(self.__check_path('t11', 't22'))
167 167
168 168 g2 = self.__update_group(g2.group_id, 'g22', parent_id=None)
169 169 Session.commit()
170 170
171 171 self.assertEqual(g2.group_name, 'g22')
172 172 # we moved out group from t1 to '' so it's full path should be 'g2'
173 173 self.assertEqual(g2.full_path, 'g22')
174 174 self.assertFalse(self.__check_path('t11', 't22'))
175 175 self.assertTrue(self.__check_path('g22'))
176 176
177 177
178 178 class TestUser(unittest.TestCase):
179 179 def __init__(self, methodName='runTest'):
180 180 Session.remove()
181 181 super(TestUser, self).__init__(methodName=methodName)
182 182
183 183 def test_create_and_remove(self):
184 usr = UserModel().create_or_update(username=u'test_user', password=u'qweqwe',
184 usr = UserModel().create_or_update(username=u'test_user',
185 password=u'qweqwe',
185 186 email=u'u232@rhodecode.org',
186 187 name=u'u1', lastname=u'u1')
187 188 Session.commit()
188 189 self.assertEqual(User.get_by_username(u'test_user'), usr)
189 190
190 191 # make users group
191 192 users_group = UsersGroupModel().create('some_example_group')
192 193 Session.commit()
193 194
194 195 UsersGroupModel().add_user_to_group(users_group, usr)
195 196 Session.commit()
196 197
197 198 self.assertEqual(UsersGroup.get(users_group.users_group_id), users_group)
198 199 self.assertEqual(UsersGroupMember.query().count(), 1)
199 200 UserModel().delete(usr.user_id)
200 201 Session.commit()
201 202
202 203 self.assertEqual(UsersGroupMember.query().all(), [])
203 204
205 def test_additonal_email_as_main(self):
206 usr = UserModel().create_or_update(username=u'test_user',
207 password=u'qweqwe',
208 email=u'main_email@rhodecode.org',
209 name=u'u1', lastname=u'u1')
210 Session.commit()
211
212 def do():
213 m = UserEmailMap()
214 m.email = u'main_email@rhodecode.org'
215 m.user = usr
216 Session.add(m)
217 Session.commit()
218 self.assertRaises(AttributeError, do)
219
220 UserModel().delete(usr.user_id)
221 Session.commit()
222
223 def test_extra_email_map(self):
224 usr = UserModel().create_or_update(username=u'test_user',
225 password=u'qweqwe',
226 email=u'main_email@rhodecode.org',
227 name=u'u1', lastname=u'u1')
228 Session.commit()
229
230 m = UserEmailMap()
231 m.email = u'main_email2@rhodecode.org'
232 m.user = usr
233 Session.add(m)
234 Session.commit()
235
236 u = User.get_by_email(email='main_email@rhodecode.org')
237 self.assertEqual(usr.user_id, u.user_id)
238 self.assertEqual(usr.username, u.username)
239
240 u = User.get_by_email(email='main_email2@rhodecode.org')
241 self.assertEqual(usr.user_id, u.user_id)
242 self.assertEqual(usr.username, u.username)
243 u = User.get_by_email(email='main_email3@rhodecode.org')
244 self.assertEqual(None, u)
245
246 UserModel().delete(usr.user_id)
247 Session.commit()
248
204 249
205 250 class TestNotifications(unittest.TestCase):
206 251
207 252 def __init__(self, methodName='runTest'):
208 253 Session.remove()
209 254 self.u1 = UserModel().create_or_update(username=u'u1',
210 255 password=u'qweqwe',
211 256 email=u'u1@rhodecode.org',
212 257 name=u'u1', lastname=u'u1')
213 258 Session.commit()
214 259 self.u1 = self.u1.user_id
215 260
216 261 self.u2 = UserModel().create_or_update(username=u'u2',
217 262 password=u'qweqwe',
218 263 email=u'u2@rhodecode.org',
219 264 name=u'u2', lastname=u'u3')
220 265 Session.commit()
221 266 self.u2 = self.u2.user_id
222 267
223 268 self.u3 = UserModel().create_or_update(username=u'u3',
224 269 password=u'qweqwe',
225 270 email=u'u3@rhodecode.org',
226 271 name=u'u3', lastname=u'u3')
227 272 Session.commit()
228 273 self.u3 = self.u3.user_id
229 274
230 275 super(TestNotifications, self).__init__(methodName=methodName)
231 276
232 277 def _clean_notifications(self):
233 278 for n in Notification.query().all():
234 279 Session.delete(n)
235 280
236 281 Session.commit()
237 282 self.assertEqual(Notification.query().all(), [])
238 283
239 284 def tearDown(self):
240 285 self._clean_notifications()
241 286
242 287 def test_create_notification(self):
243 288 self.assertEqual([], Notification.query().all())
244 289 self.assertEqual([], UserNotification.query().all())
245 290
246 291 usrs = [self.u1, self.u2]
247 292 notification = NotificationModel().create(created_by=self.u1,
248 293 subject=u'subj', body=u'hi there',
249 294 recipients=usrs)
250 295 Session.commit()
251 296 u1 = User.get(self.u1)
252 297 u2 = User.get(self.u2)
253 298 u3 = User.get(self.u3)
254 299 notifications = Notification.query().all()
255 300 self.assertEqual(len(notifications), 1)
256 301
257 302 unotification = UserNotification.query()\
258 303 .filter(UserNotification.notification == notification).all()
259 304
260 305 self.assertEqual(notifications[0].recipients, [u1, u2])
261 306 self.assertEqual(notification.notification_id,
262 307 notifications[0].notification_id)
263 308 self.assertEqual(len(unotification), len(usrs))
264 309 self.assertEqual([x.user.user_id for x in unotification], usrs)
265 310
266 311 def test_user_notifications(self):
267 312 self.assertEqual([], Notification.query().all())
268 313 self.assertEqual([], UserNotification.query().all())
269 314
270 315 notification1 = NotificationModel().create(created_by=self.u1,
271 316 subject=u'subj', body=u'hi there1',
272 317 recipients=[self.u3])
273 318 Session.commit()
274 319 notification2 = NotificationModel().create(created_by=self.u1,
275 320 subject=u'subj', body=u'hi there2',
276 321 recipients=[self.u3])
277 322 Session.commit()
278 323 u3 = Session.query(User).get(self.u3)
279 324
280 325 self.assertEqual(sorted([x.notification for x in u3.notifications]),
281 326 sorted([notification2, notification1]))
282 327
283 328 def test_delete_notifications(self):
284 329 self.assertEqual([], Notification.query().all())
285 330 self.assertEqual([], UserNotification.query().all())
286 331
287 332 notification = NotificationModel().create(created_by=self.u1,
288 333 subject=u'title', body=u'hi there3',
289 334 recipients=[self.u3, self.u1, self.u2])
290 335 Session.commit()
291 336 notifications = Notification.query().all()
292 337 self.assertTrue(notification in notifications)
293 338
294 339 Notification.delete(notification.notification_id)
295 340 Session.commit()
296 341
297 342 notifications = Notification.query().all()
298 343 self.assertFalse(notification in notifications)
299 344
300 345 un = UserNotification.query().filter(UserNotification.notification
301 346 == notification).all()
302 347 self.assertEqual(un, [])
303 348
304 349 def test_delete_association(self):
305 350
306 351 self.assertEqual([], Notification.query().all())
307 352 self.assertEqual([], UserNotification.query().all())
308 353
309 354 notification = NotificationModel().create(created_by=self.u1,
310 355 subject=u'title', body=u'hi there3',
311 356 recipients=[self.u3, self.u1, self.u2])
312 357 Session.commit()
313 358
314 359 unotification = UserNotification.query()\
315 360 .filter(UserNotification.notification ==
316 361 notification)\
317 362 .filter(UserNotification.user_id == self.u3)\
318 363 .scalar()
319 364
320 365 self.assertEqual(unotification.user_id, self.u3)
321 366
322 367 NotificationModel().delete(self.u3,
323 368 notification.notification_id)
324 369 Session.commit()
325 370
326 371 u3notification = UserNotification.query()\
327 372 .filter(UserNotification.notification ==
328 373 notification)\
329 374 .filter(UserNotification.user_id == self.u3)\
330 375 .scalar()
331 376
332 377 self.assertEqual(u3notification, None)
333 378
334 379 # notification object is still there
335 380 self.assertEqual(Notification.query().all(), [notification])
336 381
337 382 #u1 and u2 still have assignments
338 383 u1notification = UserNotification.query()\
339 384 .filter(UserNotification.notification ==
340 385 notification)\
341 386 .filter(UserNotification.user_id == self.u1)\
342 387 .scalar()
343 388 self.assertNotEqual(u1notification, None)
344 389 u2notification = UserNotification.query()\
345 390 .filter(UserNotification.notification ==
346 391 notification)\
347 392 .filter(UserNotification.user_id == self.u2)\
348 393 .scalar()
349 394 self.assertNotEqual(u2notification, None)
350 395
351 396 def test_notification_counter(self):
352 397 self._clean_notifications()
353 398 self.assertEqual([], Notification.query().all())
354 399 self.assertEqual([], UserNotification.query().all())
355 400
356 401 NotificationModel().create(created_by=self.u1,
357 402 subject=u'title', body=u'hi there_delete',
358 403 recipients=[self.u3, self.u1])
359 404 Session.commit()
360 405
361 406 self.assertEqual(NotificationModel()
362 407 .get_unread_cnt_for_user(self.u1), 1)
363 408 self.assertEqual(NotificationModel()
364 409 .get_unread_cnt_for_user(self.u2), 0)
365 410 self.assertEqual(NotificationModel()
366 411 .get_unread_cnt_for_user(self.u3), 1)
367 412
368 413 notification = NotificationModel().create(created_by=self.u1,
369 414 subject=u'title', body=u'hi there3',
370 415 recipients=[self.u3, self.u1, self.u2])
371 416 Session.commit()
372 417
373 418 self.assertEqual(NotificationModel()
374 419 .get_unread_cnt_for_user(self.u1), 2)
375 420 self.assertEqual(NotificationModel()
376 421 .get_unread_cnt_for_user(self.u2), 1)
377 422 self.assertEqual(NotificationModel()
378 423 .get_unread_cnt_for_user(self.u3), 2)
379 424
380 425
381 426 class TestUsers(unittest.TestCase):
382 427
383 428 def __init__(self, methodName='runTest'):
384 429 super(TestUsers, self).__init__(methodName=methodName)
385 430
386 431 def setUp(self):
387 432 self.u1 = UserModel().create_or_update(username=u'u1',
388 433 password=u'qweqwe',
389 434 email=u'u1@rhodecode.org',
390 435 name=u'u1', lastname=u'u1')
391 436
392 437 def tearDown(self):
393 438 perm = Permission.query().all()
394 439 for p in perm:
395 440 UserModel().revoke_perm(self.u1, p)
396 441
397 442 UserModel().delete(self.u1)
398 443 Session.commit()
399 444
400 445 def test_add_perm(self):
401 446 perm = Permission.query().all()[0]
402 447 UserModel().grant_perm(self.u1, perm)
403 448 Session.commit()
404 449 self.assertEqual(UserModel().has_perm(self.u1, perm), True)
405 450
406 451 def test_has_perm(self):
407 452 perm = Permission.query().all()
408 453 for p in perm:
409 454 has_p = UserModel().has_perm(self.u1, p)
410 455 self.assertEqual(False, has_p)
411 456
412 457 def test_revoke_perm(self):
413 458 perm = Permission.query().all()[0]
414 459 UserModel().grant_perm(self.u1, perm)
415 460 Session.commit()
416 461 self.assertEqual(UserModel().has_perm(self.u1, perm), True)
417 462
418 463 #revoke
419 464 UserModel().revoke_perm(self.u1, perm)
420 465 Session.commit()
421 466 self.assertEqual(UserModel().has_perm(self.u1, perm), False)
422 467
423 468
424 469 class TestPermissions(unittest.TestCase):
425 470 def __init__(self, methodName='runTest'):
426 471 super(TestPermissions, self).__init__(methodName=methodName)
427 472
428 473 def setUp(self):
429 474 self.u1 = UserModel().create_or_update(
430 475 username=u'u1', password=u'qweqwe',
431 476 email=u'u1@rhodecode.org', name=u'u1', lastname=u'u1'
432 477 )
433 478 self.u2 = UserModel().create_or_update(
434 479 username=u'u2', password=u'qweqwe',
435 480 email=u'u2@rhodecode.org', name=u'u2', lastname=u'u2'
436 481 )
437 482 self.anon = User.get_by_username('default')
438 483 self.a1 = UserModel().create_or_update(
439 484 username=u'a1', password=u'qweqwe',
440 485 email=u'a1@rhodecode.org', name=u'a1', lastname=u'a1', admin=True
441 486 )
442 487 Session.commit()
443 488
444 489 def tearDown(self):
445 490 if hasattr(self, 'test_repo'):
446 491 RepoModel().delete(repo=self.test_repo)
447 492 UserModel().delete(self.u1)
448 493 UserModel().delete(self.u2)
449 494 UserModel().delete(self.a1)
450 495 if hasattr(self, 'g1'):
451 496 ReposGroupModel().delete(self.g1.group_id)
452 497 if hasattr(self, 'g2'):
453 498 ReposGroupModel().delete(self.g2.group_id)
454 499
455 500 if hasattr(self, 'ug1'):
456 501 UsersGroupModel().delete(self.ug1, force=True)
457 502
458 503 Session.commit()
459 504
460 505 def test_default_perms_set(self):
461 506 u1_auth = AuthUser(user_id=self.u1.user_id)
462 507 perms = {
463 508 'repositories_groups': {},
464 509 'global': set([u'hg.create.repository', u'repository.read',
465 510 u'hg.register.manual_activate']),
466 511 'repositories': {u'vcs_test_hg': u'repository.read'}
467 512 }
468 513 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
469 514 perms['repositories'][HG_REPO])
470 515 new_perm = 'repository.write'
471 516 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm)
472 517 Session.commit()
473 518
474 519 u1_auth = AuthUser(user_id=self.u1.user_id)
475 520 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO], new_perm)
476 521
477 522 def test_default_admin_perms_set(self):
478 523 a1_auth = AuthUser(user_id=self.a1.user_id)
479 524 perms = {
480 525 'repositories_groups': {},
481 526 'global': set([u'hg.admin']),
482 527 'repositories': {u'vcs_test_hg': u'repository.admin'}
483 528 }
484 529 self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
485 530 perms['repositories'][HG_REPO])
486 531 new_perm = 'repository.write'
487 532 RepoModel().grant_user_permission(repo=HG_REPO, user=self.a1, perm=new_perm)
488 533 Session.commit()
489 534 # cannot really downgrade admins permissions !? they still get's set as
490 535 # admin !
491 536 u1_auth = AuthUser(user_id=self.a1.user_id)
492 537 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
493 538 perms['repositories'][HG_REPO])
494 539
495 540 def test_default_group_perms(self):
496 541 self.g1 = _make_group('test1', skip_if_exists=True)
497 542 self.g2 = _make_group('test2', skip_if_exists=True)
498 543 u1_auth = AuthUser(user_id=self.u1.user_id)
499 544 perms = {
500 545 'repositories_groups': {u'test1': 'group.read', u'test2': 'group.read'},
501 546 'global': set([u'hg.create.repository', u'repository.read', u'hg.register.manual_activate']),
502 547 'repositories': {u'vcs_test_hg': u'repository.read'}
503 548 }
504 549 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
505 550 perms['repositories'][HG_REPO])
506 551 self.assertEqual(u1_auth.permissions['repositories_groups'],
507 552 perms['repositories_groups'])
508 553
509 554 def test_default_admin_group_perms(self):
510 555 self.g1 = _make_group('test1', skip_if_exists=True)
511 556 self.g2 = _make_group('test2', skip_if_exists=True)
512 557 a1_auth = AuthUser(user_id=self.a1.user_id)
513 558 perms = {
514 559 'repositories_groups': {u'test1': 'group.admin', u'test2': 'group.admin'},
515 560 'global': set(['hg.admin']),
516 561 'repositories': {u'vcs_test_hg': 'repository.admin'}
517 562 }
518 563
519 564 self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
520 565 perms['repositories'][HG_REPO])
521 566 self.assertEqual(a1_auth.permissions['repositories_groups'],
522 567 perms['repositories_groups'])
523 568
524 569 def test_propagated_permission_from_users_group(self):
525 570 # make group
526 571 self.ug1 = UsersGroupModel().create('G1')
527 572 # add user to group
528 573 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
529 574
530 575 # set permission to lower
531 576 new_perm = 'repository.none'
532 577 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm)
533 578 Session.commit()
534 579 u1_auth = AuthUser(user_id=self.u1.user_id)
535 580 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
536 581 new_perm)
537 582
538 583 # grant perm for group this should override permission from user
539 584 new_perm = 'repository.write'
540 585 RepoModel().grant_users_group_permission(repo=HG_REPO,
541 586 group_name=self.ug1,
542 587 perm=new_perm)
543 588 # check perms
544 589 u1_auth = AuthUser(user_id=self.u1.user_id)
545 590 perms = {
546 591 'repositories_groups': {},
547 592 'global': set([u'hg.create.repository', u'repository.read',
548 593 u'hg.register.manual_activate']),
549 594 'repositories': {u'vcs_test_hg': u'repository.read'}
550 595 }
551 596 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
552 597 new_perm)
553 598 self.assertEqual(u1_auth.permissions['repositories_groups'],
554 599 perms['repositories_groups'])
555 600
556 601 def test_propagated_permission_from_users_group_lower_weight(self):
557 602 # make group
558 603 self.ug1 = UsersGroupModel().create('G1')
559 604 # add user to group
560 605 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
561 606
562 607 # set permission to lower
563 608 new_perm_h = 'repository.write'
564 609 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1,
565 610 perm=new_perm_h)
566 611 Session.commit()
567 612 u1_auth = AuthUser(user_id=self.u1.user_id)
568 613 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
569 614 new_perm_h)
570 615
571 616 # grant perm for group this should NOT override permission from user
572 617 # since it's lower than granted
573 618 new_perm_l = 'repository.read'
574 619 RepoModel().grant_users_group_permission(repo=HG_REPO,
575 620 group_name=self.ug1,
576 621 perm=new_perm_l)
577 622 # check perms
578 623 u1_auth = AuthUser(user_id=self.u1.user_id)
579 624 perms = {
580 625 'repositories_groups': {},
581 626 'global': set([u'hg.create.repository', u'repository.read',
582 627 u'hg.register.manual_activate']),
583 628 'repositories': {u'vcs_test_hg': u'repository.write'}
584 629 }
585 630 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
586 631 new_perm_h)
587 632 self.assertEqual(u1_auth.permissions['repositories_groups'],
588 633 perms['repositories_groups'])
589 634
590 635 def test_repo_in_group_permissions(self):
591 636 self.g1 = _make_group('group1', skip_if_exists=True)
592 637 self.g2 = _make_group('group2', skip_if_exists=True)
593 638 Session.commit()
594 639 # both perms should be read !
595 640 u1_auth = AuthUser(user_id=self.u1.user_id)
596 641 self.assertEqual(u1_auth.permissions['repositories_groups'],
597 642 {u'group1': u'group.read', u'group2': u'group.read'})
598 643
599 644 a1_auth = AuthUser(user_id=self.anon.user_id)
600 645 self.assertEqual(a1_auth.permissions['repositories_groups'],
601 646 {u'group1': u'group.read', u'group2': u'group.read'})
602 647
603 648 #Change perms to none for both groups
604 649 ReposGroupModel().grant_user_permission(repos_group=self.g1,
605 650 user=self.anon,
606 651 perm='group.none')
607 652 ReposGroupModel().grant_user_permission(repos_group=self.g2,
608 653 user=self.anon,
609 654 perm='group.none')
610 655
611 656
612 657 u1_auth = AuthUser(user_id=self.u1.user_id)
613 658 self.assertEqual(u1_auth.permissions['repositories_groups'],
614 659 {u'group1': u'group.none', u'group2': u'group.none'})
615 660
616 661 a1_auth = AuthUser(user_id=self.anon.user_id)
617 662 self.assertEqual(a1_auth.permissions['repositories_groups'],
618 663 {u'group1': u'group.none', u'group2': u'group.none'})
619 664
620 665 # add repo to group
621 666 form_data = {
622 667 'repo_name':HG_REPO,
623 668 'repo_name_full':RepoGroup.url_sep().join([self.g1.group_name,HG_REPO]),
624 669 'repo_type':'hg',
625 670 'clone_uri':'',
626 671 'repo_group':self.g1.group_id,
627 672 'description':'desc',
628 673 'private':False
629 674 }
630 675 self.test_repo = RepoModel().create(form_data, cur_user=self.u1)
631 676 Session.commit()
632 677
633 678 u1_auth = AuthUser(user_id=self.u1.user_id)
634 679 self.assertEqual(u1_auth.permissions['repositories_groups'],
635 680 {u'group1': u'group.none', u'group2': u'group.none'})
636 681
637 682 a1_auth = AuthUser(user_id=self.anon.user_id)
638 683 self.assertEqual(a1_auth.permissions['repositories_groups'],
639 684 {u'group1': u'group.none', u'group2': u'group.none'})
640 685
641 686 #grant permission for u2 !
642 687 ReposGroupModel().grant_user_permission(repos_group=self.g1,
643 688 user=self.u2,
644 689 perm='group.read')
645 690 ReposGroupModel().grant_user_permission(repos_group=self.g2,
646 691 user=self.u2,
647 692 perm='group.read')
648 693 Session.commit()
649 694 self.assertNotEqual(self.u1, self.u2)
650 695 #u1 and anon should have not change perms while u2 should !
651 696 u1_auth = AuthUser(user_id=self.u1.user_id)
652 697 self.assertEqual(u1_auth.permissions['repositories_groups'],
653 698 {u'group1': u'group.none', u'group2': u'group.none'})
654 699
655 700 u2_auth = AuthUser(user_id=self.u2.user_id)
656 701 self.assertEqual(u2_auth.permissions['repositories_groups'],
657 702 {u'group1': u'group.read', u'group2': u'group.read'})
658 703
659 704 a1_auth = AuthUser(user_id=self.anon.user_id)
660 705 self.assertEqual(a1_auth.permissions['repositories_groups'],
661 706 {u'group1': u'group.none', u'group2': u'group.none'})
662 707
663 708 def test_repo_group_user_as_user_group_member(self):
664 709 # create Group1
665 710 self.g1 = _make_group('group1', skip_if_exists=True)
666 711 Session.commit()
667 712 a1_auth = AuthUser(user_id=self.anon.user_id)
668 713
669 714 self.assertEqual(a1_auth.permissions['repositories_groups'],
670 715 {u'group1': u'group.read'})
671 716
672 717 # set default permission to none
673 718 ReposGroupModel().grant_user_permission(repos_group=self.g1,
674 719 user=self.anon,
675 720 perm='group.none')
676 721 # make group
677 722 self.ug1 = UsersGroupModel().create('G1')
678 723 # add user to group
679 724 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
680 725 Session.commit()
681 726
682 727 # check if user is in the group
683 728 membrs = [x.user_id for x in UsersGroupModel().get(self.ug1.users_group_id).members]
684 729 self.assertEqual(membrs, [self.u1.user_id])
685 730 # add some user to that group
686 731
687 732 # check his permissions
688 733 a1_auth = AuthUser(user_id=self.anon.user_id)
689 734 self.assertEqual(a1_auth.permissions['repositories_groups'],
690 735 {u'group1': u'group.none'})
691 736
692 737 u1_auth = AuthUser(user_id=self.u1.user_id)
693 738 self.assertEqual(u1_auth.permissions['repositories_groups'],
694 739 {u'group1': u'group.none'})
695 740
696 741 # grant ug1 read permissions for
697 742 ReposGroupModel().grant_users_group_permission(repos_group=self.g1,
698 743 group_name=self.ug1,
699 744 perm='group.read')
700 745 Session.commit()
701 746 # check if the
702 747 obj = Session.query(UsersGroupRepoGroupToPerm)\
703 748 .filter(UsersGroupRepoGroupToPerm.group == self.g1)\
704 749 .filter(UsersGroupRepoGroupToPerm.users_group == self.ug1)\
705 750 .scalar()
706 751 self.assertEqual(obj.permission.permission_name, 'group.read')
707 752
708 753 a1_auth = AuthUser(user_id=self.anon.user_id)
709 754
710 755 self.assertEqual(a1_auth.permissions['repositories_groups'],
711 756 {u'group1': u'group.none'})
712 757
713 758 u1_auth = AuthUser(user_id=self.u1.user_id)
714 759 self.assertEqual(u1_auth.permissions['repositories_groups'],
715 760 {u'group1': u'group.read'})
General Comments 0
You need to be logged in to leave comments. Login now