##// END OF EJS Templates
fixes #298, ldap email addresses created by rhodecode automatically during first login didn't get converted to lower case, which lead to lookup failures and than wrong checks for uniqueness. Fixed that by putting a setter on db model column that will enforce converting to lowercase.
marcink -
r1757:2aa7f454 beta
parent child Browse files
Show More
@@ -1,1097 +1,1108 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) 2009-2011 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
31 31 from sqlalchemy import *
32 32 from sqlalchemy.ext.hybrid import hybrid_property
33 33 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
34 34 from beaker.cache import cache_region, region_invalidate
35 35
36 36 from vcs import get_backend
37 37 from vcs.utils.helpers import get_scm
38 38 from vcs.exceptions import VCSError
39 39 from vcs.utils.lazy import LazyProperty
40 40
41 41 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, safe_unicode
42 42 from rhodecode.lib.exceptions import UsersGroupsAssignedException
43 43 from rhodecode.lib.compat import json
44 44 from rhodecode.lib.caching_query import FromCache
45 45
46 46 from rhodecode.model.meta import Base, Session
47 47
48 48 log = logging.getLogger(__name__)
49 49
50 50 #==============================================================================
51 51 # BASE CLASSES
52 52 #==============================================================================
53 53
54 54 class ModelSerializer(json.JSONEncoder):
55 55 """
56 56 Simple Serializer for JSON,
57 57
58 58 usage::
59 59
60 60 to make object customized for serialization implement a __json__
61 61 method that will return a dict for serialization into json
62 62
63 63 example::
64 64
65 65 class Task(object):
66 66
67 67 def __init__(self, name, value):
68 68 self.name = name
69 69 self.value = value
70 70
71 71 def __json__(self):
72 72 return dict(name=self.name,
73 73 value=self.value)
74 74
75 75 """
76 76
77 77 def default(self, obj):
78 78
79 79 if hasattr(obj, '__json__'):
80 80 return obj.__json__()
81 81 else:
82 82 return json.JSONEncoder.default(self, obj)
83 83
84 84 class BaseModel(object):
85 85 """Base Model for all classess
86 86
87 87 """
88 88
89 89 @classmethod
90 90 def _get_keys(cls):
91 91 """return column names for this model """
92 92 return class_mapper(cls).c.keys()
93 93
94 94 def get_dict(self):
95 95 """return dict with keys and values corresponding
96 96 to this model data """
97 97
98 98 d = {}
99 99 for k in self._get_keys():
100 100 d[k] = getattr(self, k)
101 101 return d
102 102
103 103 def get_appstruct(self):
104 104 """return list with keys and values tupples corresponding
105 105 to this model data """
106 106
107 107 l = []
108 108 for k in self._get_keys():
109 109 l.append((k, getattr(self, k),))
110 110 return l
111 111
112 112 def populate_obj(self, populate_dict):
113 113 """populate model with data from given populate_dict"""
114 114
115 115 for k in self._get_keys():
116 116 if k in populate_dict:
117 117 setattr(self, k, populate_dict[k])
118 118
119 119 @classmethod
120 120 def query(cls):
121 121 return Session.query(cls)
122 122
123 123 @classmethod
124 124 def get(cls, id_):
125 125 if id_:
126 126 return cls.query().get(id_)
127 127
128 128 @classmethod
129 129 def getAll(cls):
130 130 return cls.query().all()
131 131
132 132 @classmethod
133 133 def delete(cls, id_):
134 134 obj = cls.query().get(id_)
135 135 Session.delete(obj)
136 136
137 137
138 138 class RhodeCodeSetting(Base, BaseModel):
139 139 __tablename__ = 'rhodecode_settings'
140 140 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
141 141 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
142 142 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
143 143 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
144 144
145 145 def __init__(self, k='', v=''):
146 146 self.app_settings_name = k
147 147 self.app_settings_value = v
148 148
149 149
150 150 @validates('_app_settings_value')
151 151 def validate_settings_value(self, key, val):
152 152 assert type(val) == unicode
153 153 return val
154 154
155 155 @hybrid_property
156 156 def app_settings_value(self):
157 157 v = self._app_settings_value
158 158 if v == 'ldap_active':
159 159 v = str2bool(v)
160 160 return v
161 161
162 162 @app_settings_value.setter
163 163 def app_settings_value(self, val):
164 164 """
165 165 Setter that will always make sure we use unicode in app_settings_value
166 166
167 167 :param val:
168 168 """
169 169 self._app_settings_value = safe_unicode(val)
170 170
171 171 def __repr__(self):
172 172 return "<%s('%s:%s')>" % (self.__class__.__name__,
173 173 self.app_settings_name, self.app_settings_value)
174 174
175 175
176 176 @classmethod
177 177 def get_by_name(cls, ldap_key):
178 178 return cls.query()\
179 179 .filter(cls.app_settings_name == ldap_key).scalar()
180 180
181 181 @classmethod
182 182 def get_app_settings(cls, cache=False):
183 183
184 184 ret = cls.query()
185 185
186 186 if cache:
187 187 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
188 188
189 189 if not ret:
190 190 raise Exception('Could not get application settings !')
191 191 settings = {}
192 192 for each in ret:
193 193 settings['rhodecode_' + each.app_settings_name] = \
194 194 each.app_settings_value
195 195
196 196 return settings
197 197
198 198 @classmethod
199 199 def get_ldap_settings(cls, cache=False):
200 200 ret = cls.query()\
201 201 .filter(cls.app_settings_name.startswith('ldap_')).all()
202 202 fd = {}
203 203 for row in ret:
204 204 fd.update({row.app_settings_name:row.app_settings_value})
205 205
206 206 return fd
207 207
208 208
209 209 class RhodeCodeUi(Base, BaseModel):
210 210 __tablename__ = 'rhodecode_ui'
211 211 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
212 212
213 213 HOOK_UPDATE = 'changegroup.update'
214 214 HOOK_REPO_SIZE = 'changegroup.repo_size'
215 215 HOOK_PUSH = 'pretxnchangegroup.push_logger'
216 216 HOOK_PULL = 'preoutgoing.pull_logger'
217 217
218 218 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
219 219 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
220 220 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
221 221 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
222 222 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
223 223
224 224
225 225 @classmethod
226 226 def get_by_key(cls, key):
227 227 return cls.query().filter(cls.ui_key == key)
228 228
229 229
230 230 @classmethod
231 231 def get_builtin_hooks(cls):
232 232 q = cls.query()
233 233 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
234 234 cls.HOOK_REPO_SIZE,
235 235 cls.HOOK_PUSH, cls.HOOK_PULL]))
236 236 return q.all()
237 237
238 238 @classmethod
239 239 def get_custom_hooks(cls):
240 240 q = cls.query()
241 241 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
242 242 cls.HOOK_REPO_SIZE,
243 243 cls.HOOK_PUSH, cls.HOOK_PULL]))
244 244 q = q.filter(cls.ui_section == 'hooks')
245 245 return q.all()
246 246
247 247 @classmethod
248 248 def create_or_update_hook(cls, key, val):
249 249 new_ui = cls.get_by_key(key).scalar() or cls()
250 250 new_ui.ui_section = 'hooks'
251 251 new_ui.ui_active = True
252 252 new_ui.ui_key = key
253 253 new_ui.ui_value = val
254 254
255 255 Session.add(new_ui)
256 256
257 257
258 258 class User(Base, BaseModel):
259 259 __tablename__ = 'users'
260 260 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
261 261 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
262 262 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
263 263 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
264 264 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
265 265 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
266 266 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
267 267 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 269 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
270 270 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
271 271 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
272 272
273 273 user_log = relationship('UserLog', cascade='all')
274 274 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
275 275
276 276 repositories = relationship('Repository')
277 277 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
278 278 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
279 279
280 280 group_member = relationship('UsersGroupMember', cascade='all')
281 281
282 282 notifications = relationship('UserNotification',)
283 283
284 @hybrid_property
285 def email(self):
286 return self._email
287
288 @email.setter
289 def email(self, val):
290 self._email = val.lower() if val else None
291
284 292 @property
285 293 def full_name(self):
286 294 return '%s %s' % (self.name, self.lastname)
287 295
288 296 @property
289 297 def full_contact(self):
290 298 return '%s %s <%s>' % (self.name, self.lastname, self.email)
291 299
292 300 @property
293 301 def short_contact(self):
294 302 return '%s %s' % (self.name, self.lastname)
295 303
296 304 @property
297 305 def is_admin(self):
298 306 return self.admin
299 307
300 308 def __repr__(self):
301 309 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
302 310 self.user_id, self.username)
303 311
304 312
305 313 @classmethod
306 314 def get_by_username(cls, username, case_insensitive=False, cache=False):
307 315 if case_insensitive:
308 316 q = cls.query().filter(cls.username.ilike(username))
309 317 else:
310 318 q = cls.query().filter(cls.username == username)
311 319
312 320 if cache:
313 321 q = q.options(FromCache("sql_cache_short",
314 322 "get_user_%s" % username))
315 323 return q.scalar()
316 324
317 325 @classmethod
318 326 def get_by_api_key(cls, api_key, cache=False):
319 327 q = cls.query().filter(cls.api_key == api_key)
320 328
321 329 if cache:
322 330 q = q.options(FromCache("sql_cache_short",
323 331 "get_api_key_%s" % api_key))
324 332 return q.scalar()
325 333
326 334 @classmethod
327 def get_by_email(cls, email, cache=False):
335 def get_by_email(cls, email, case_insensitive=False, cache=False):
336 if case_insensitive:
337 q = cls.query().filter(cls.email.ilike(email))
338 else:
328 339 q = cls.query().filter(cls.email == email)
329 340
330 341 if cache:
331 342 q = q.options(FromCache("sql_cache_short",
332 343 "get_api_key_%s" % email))
333 344 return q.scalar()
334 345
335 346 def update_lastlogin(self):
336 347 """Update user lastlogin"""
337 348 self.last_login = datetime.datetime.now()
338 349 Session.add(self)
339 350 log.debug('updated user %s lastlogin', self.username)
340 351
341 352
342 353 class UserLog(Base, BaseModel):
343 354 __tablename__ = 'user_logs'
344 355 __table_args__ = {'extend_existing':True}
345 356 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
346 357 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
347 358 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
348 359 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
349 360 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
350 361 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
351 362 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
352 363
353 364 @property
354 365 def action_as_day(self):
355 366 return datetime.date(*self.action_date.timetuple()[:3])
356 367
357 368 user = relationship('User')
358 369 repository = relationship('Repository',cascade='')
359 370
360 371
361 372 class UsersGroup(Base, BaseModel):
362 373 __tablename__ = 'users_groups'
363 374 __table_args__ = {'extend_existing':True}
364 375
365 376 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
366 377 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
367 378 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
368 379
369 380 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
370 381
371 382 def __repr__(self):
372 383 return '<userGroup(%s)>' % (self.users_group_name)
373 384
374 385 @classmethod
375 386 def get_by_group_name(cls, group_name, cache=False,
376 387 case_insensitive=False):
377 388 if case_insensitive:
378 389 q = cls.query().filter(cls.users_group_name.ilike(group_name))
379 390 else:
380 391 q = cls.query().filter(cls.users_group_name == group_name)
381 392 if cache:
382 393 q = q.options(FromCache("sql_cache_short",
383 394 "get_user_%s" % group_name))
384 395 return q.scalar()
385 396
386 397 @classmethod
387 398 def get(cls, users_group_id, cache=False):
388 399 users_group = cls.query()
389 400 if cache:
390 401 users_group = users_group.options(FromCache("sql_cache_short",
391 402 "get_users_group_%s" % users_group_id))
392 403 return users_group.get(users_group_id)
393 404
394 405 class UsersGroupMember(Base, BaseModel):
395 406 __tablename__ = 'users_groups_members'
396 407 __table_args__ = {'extend_existing':True}
397 408
398 409 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
399 410 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
400 411 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
401 412
402 413 user = relationship('User', lazy='joined')
403 414 users_group = relationship('UsersGroup')
404 415
405 416 def __init__(self, gr_id='', u_id=''):
406 417 self.users_group_id = gr_id
407 418 self.user_id = u_id
408 419
409 420 @staticmethod
410 421 def add_user_to_group(group, user):
411 422 ugm = UsersGroupMember()
412 423 ugm.users_group = group
413 424 ugm.user = user
414 425 Session.add(ugm)
415 426 Session.commit()
416 427 return ugm
417 428
418 429 class Repository(Base, BaseModel):
419 430 __tablename__ = 'repositories'
420 431 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
421 432
422 433 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
423 434 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
424 435 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
425 436 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
426 437 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
427 438 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
428 439 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
429 440 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
430 441 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
431 442 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
432 443
433 444 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
434 445 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
435 446
436 447
437 448 user = relationship('User')
438 449 fork = relationship('Repository', remote_side=repo_id)
439 450 group = relationship('RepoGroup')
440 451 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
441 452 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
442 453 stats = relationship('Statistics', cascade='all', uselist=False)
443 454
444 455 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
445 456
446 457 logs = relationship('UserLog')
447 458
448 459 def __repr__(self):
449 460 return "<%s('%s:%s')>" % (self.__class__.__name__,
450 461 self.repo_id, self.repo_name)
451 462
452 463 @classmethod
453 464 def url_sep(cls):
454 465 return '/'
455 466
456 467 @classmethod
457 468 def get_by_repo_name(cls, repo_name):
458 469 q = Session.query(cls).filter(cls.repo_name == repo_name)
459 470 q = q.options(joinedload(Repository.fork))\
460 471 .options(joinedload(Repository.user))\
461 472 .options(joinedload(Repository.group))
462 473 return q.scalar()
463 474
464 475 @classmethod
465 476 def get_repo_forks(cls, repo_id):
466 477 return cls.query().filter(Repository.fork_id == repo_id)
467 478
468 479 @classmethod
469 480 def base_path(cls):
470 481 """
471 482 Returns base path when all repos are stored
472 483
473 484 :param cls:
474 485 """
475 486 q = Session.query(RhodeCodeUi)\
476 487 .filter(RhodeCodeUi.ui_key == cls.url_sep())
477 488 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
478 489 return q.one().ui_value
479 490
480 491 @property
481 492 def just_name(self):
482 493 return self.repo_name.split(Repository.url_sep())[-1]
483 494
484 495 @property
485 496 def groups_with_parents(self):
486 497 groups = []
487 498 if self.group is None:
488 499 return groups
489 500
490 501 cur_gr = self.group
491 502 groups.insert(0, cur_gr)
492 503 while 1:
493 504 gr = getattr(cur_gr, 'parent_group', None)
494 505 cur_gr = cur_gr.parent_group
495 506 if gr is None:
496 507 break
497 508 groups.insert(0, gr)
498 509
499 510 return groups
500 511
501 512 @property
502 513 def groups_and_repo(self):
503 514 return self.groups_with_parents, self.just_name
504 515
505 516 @LazyProperty
506 517 def repo_path(self):
507 518 """
508 519 Returns base full path for that repository means where it actually
509 520 exists on a filesystem
510 521 """
511 522 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
512 523 Repository.url_sep())
513 524 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
514 525 return q.one().ui_value
515 526
516 527 @property
517 528 def repo_full_path(self):
518 529 p = [self.repo_path]
519 530 # we need to split the name by / since this is how we store the
520 531 # names in the database, but that eventually needs to be converted
521 532 # into a valid system path
522 533 p += self.repo_name.split(Repository.url_sep())
523 534 return os.path.join(*p)
524 535
525 536 def get_new_name(self, repo_name):
526 537 """
527 538 returns new full repository name based on assigned group and new new
528 539
529 540 :param group_name:
530 541 """
531 542 path_prefix = self.group.full_path_splitted if self.group else []
532 543 return Repository.url_sep().join(path_prefix + [repo_name])
533 544
534 545 @property
535 546 def _ui(self):
536 547 """
537 548 Creates an db based ui object for this repository
538 549 """
539 550 from mercurial import ui
540 551 from mercurial import config
541 552 baseui = ui.ui()
542 553
543 554 #clean the baseui object
544 555 baseui._ocfg = config.config()
545 556 baseui._ucfg = config.config()
546 557 baseui._tcfg = config.config()
547 558
548 559
549 560 ret = RhodeCodeUi.query()\
550 561 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
551 562
552 563 hg_ui = ret
553 564 for ui_ in hg_ui:
554 565 if ui_.ui_active:
555 566 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
556 567 ui_.ui_key, ui_.ui_value)
557 568 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
558 569
559 570 return baseui
560 571
561 572 @classmethod
562 573 def is_valid(cls, repo_name):
563 574 """
564 575 returns True if given repo name is a valid filesystem repository
565 576
566 577 @param cls:
567 578 @param repo_name:
568 579 """
569 580 from rhodecode.lib.utils import is_valid_repo
570 581
571 582 return is_valid_repo(repo_name, cls.base_path())
572 583
573 584
574 585 #==========================================================================
575 586 # SCM PROPERTIES
576 587 #==========================================================================
577 588
578 589 def get_changeset(self, rev):
579 590 return get_changeset_safe(self.scm_instance, rev)
580 591
581 592 @property
582 593 def tip(self):
583 594 return self.get_changeset('tip')
584 595
585 596 @property
586 597 def author(self):
587 598 return self.tip.author
588 599
589 600 @property
590 601 def last_change(self):
591 602 return self.scm_instance.last_change
592 603
593 604 #==========================================================================
594 605 # SCM CACHE INSTANCE
595 606 #==========================================================================
596 607
597 608 @property
598 609 def invalidate(self):
599 610 return CacheInvalidation.invalidate(self.repo_name)
600 611
601 612 def set_invalidate(self):
602 613 """
603 614 set a cache for invalidation for this instance
604 615 """
605 616 CacheInvalidation.set_invalidate(self.repo_name)
606 617
607 618 @LazyProperty
608 619 def scm_instance(self):
609 620 return self.__get_instance()
610 621
611 622 @property
612 623 def scm_instance_cached(self):
613 624 @cache_region('long_term')
614 625 def _c(repo_name):
615 626 return self.__get_instance()
616 627 rn = self.repo_name
617 628 log.debug('Getting cached instance of repo')
618 629 inv = self.invalidate
619 630 if inv is not None:
620 631 region_invalidate(_c, None, rn)
621 632 # update our cache
622 633 CacheInvalidation.set_valid(inv.cache_key)
623 634 return _c(rn)
624 635
625 636 def __get_instance(self):
626 637 repo_full_path = self.repo_full_path
627 638 try:
628 639 alias = get_scm(repo_full_path)[0]
629 640 log.debug('Creating instance of %s repository', alias)
630 641 backend = get_backend(alias)
631 642 except VCSError:
632 643 log.error(traceback.format_exc())
633 644 log.error('Perhaps this repository is in db and not in '
634 645 'filesystem run rescan repositories with '
635 646 '"destroy old data " option from admin panel')
636 647 return
637 648
638 649 if alias == 'hg':
639 650 repo = backend(safe_str(repo_full_path), create=False,
640 651 baseui=self._ui)
641 652 # skip hidden web repository
642 653 if repo._get_hidden():
643 654 return
644 655 else:
645 656 repo = backend(repo_full_path, create=False)
646 657
647 658 return repo
648 659
649 660
650 661 class RepoGroup(Base, BaseModel):
651 662 __tablename__ = 'groups'
652 663 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
653 664 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
654 665 __mapper_args__ = {'order_by':'group_name'}
655 666
656 667 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
657 668 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
658 669 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
659 670 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
660 671
661 672 parent_group = relationship('RepoGroup', remote_side=group_id)
662 673
663 674
664 675 def __init__(self, group_name='', parent_group=None):
665 676 self.group_name = group_name
666 677 self.parent_group = parent_group
667 678
668 679 def __repr__(self):
669 680 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
670 681 self.group_name)
671 682
672 683 @classmethod
673 684 def groups_choices(cls):
674 685 from webhelpers.html import literal as _literal
675 686 repo_groups = [('', '')]
676 687 sep = ' &raquo; '
677 688 _name = lambda k: _literal(sep.join(k))
678 689
679 690 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
680 691 for x in cls.query().all()])
681 692
682 693 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
683 694 return repo_groups
684 695
685 696 @classmethod
686 697 def url_sep(cls):
687 698 return '/'
688 699
689 700 @classmethod
690 701 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
691 702 if case_insensitive:
692 703 gr = cls.query()\
693 704 .filter(cls.group_name.ilike(group_name))
694 705 else:
695 706 gr = cls.query()\
696 707 .filter(cls.group_name == group_name)
697 708 if cache:
698 709 gr = gr.options(FromCache("sql_cache_short",
699 710 "get_group_%s" % group_name))
700 711 return gr.scalar()
701 712
702 713 @property
703 714 def parents(self):
704 715 parents_recursion_limit = 5
705 716 groups = []
706 717 if self.parent_group is None:
707 718 return groups
708 719 cur_gr = self.parent_group
709 720 groups.insert(0, cur_gr)
710 721 cnt = 0
711 722 while 1:
712 723 cnt += 1
713 724 gr = getattr(cur_gr, 'parent_group', None)
714 725 cur_gr = cur_gr.parent_group
715 726 if gr is None:
716 727 break
717 728 if cnt == parents_recursion_limit:
718 729 # this will prevent accidental infinit loops
719 730 log.error('group nested more than %s' %
720 731 parents_recursion_limit)
721 732 break
722 733
723 734 groups.insert(0, gr)
724 735 return groups
725 736
726 737 @property
727 738 def children(self):
728 739 return RepoGroup.query().filter(RepoGroup.parent_group == self)
729 740
730 741 @property
731 742 def name(self):
732 743 return self.group_name.split(RepoGroup.url_sep())[-1]
733 744
734 745 @property
735 746 def full_path(self):
736 747 return self.group_name
737 748
738 749 @property
739 750 def full_path_splitted(self):
740 751 return self.group_name.split(RepoGroup.url_sep())
741 752
742 753 @property
743 754 def repositories(self):
744 755 return Repository.query().filter(Repository.group == self)
745 756
746 757 @property
747 758 def repositories_recursive_count(self):
748 759 cnt = self.repositories.count()
749 760
750 761 def children_count(group):
751 762 cnt = 0
752 763 for child in group.children:
753 764 cnt += child.repositories.count()
754 765 cnt += children_count(child)
755 766 return cnt
756 767
757 768 return cnt + children_count(self)
758 769
759 770
760 771 def get_new_name(self, group_name):
761 772 """
762 773 returns new full group name based on parent and new name
763 774
764 775 :param group_name:
765 776 """
766 777 path_prefix = (self.parent_group.full_path_splitted if
767 778 self.parent_group else [])
768 779 return RepoGroup.url_sep().join(path_prefix + [group_name])
769 780
770 781
771 782 class Permission(Base, BaseModel):
772 783 __tablename__ = 'permissions'
773 784 __table_args__ = {'extend_existing':True}
774 785 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
775 786 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
776 787 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
777 788
778 789 def __repr__(self):
779 790 return "<%s('%s:%s')>" % (self.__class__.__name__,
780 791 self.permission_id, self.permission_name)
781 792
782 793 @classmethod
783 794 def get_by_key(cls, key):
784 795 return cls.query().filter(cls.permission_name == key).scalar()
785 796
786 797 @classmethod
787 798 def get_default_perms(cls, default_user_id):
788 799 q = Session.query(UserRepoToPerm, Repository, cls)\
789 800 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
790 801 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
791 802 .filter(UserRepoToPerm.user_id == default_user_id)
792 803
793 804 return q.all()
794 805
795 806
796 807 class UserRepoToPerm(Base, BaseModel):
797 808 __tablename__ = 'repo_to_perm'
798 809 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
799 810 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
800 811 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
801 812 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
802 813 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
803 814
804 815 user = relationship('User')
805 816 permission = relationship('Permission')
806 817 repository = relationship('Repository')
807 818
808 819 @classmethod
809 820 def create(cls, user, repository, permission):
810 821 n = cls()
811 822 n.user = user
812 823 n.repository = repository
813 824 n.permission = permission
814 825 Session.add(n)
815 826 return n
816 827
817 828 def __repr__(self):
818 829 return '<user:%s => %s >' % (self.user, self.repository)
819 830
820 831 class UserToPerm(Base, BaseModel):
821 832 __tablename__ = 'user_to_perm'
822 833 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
823 834 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
824 835 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
825 836 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
826 837
827 838 user = relationship('User')
828 839 permission = relationship('Permission', lazy='joined')
829 840
830 841
831 842 class UsersGroupRepoToPerm(Base, BaseModel):
832 843 __tablename__ = 'users_group_repo_to_perm'
833 844 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
834 845 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
835 846 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
836 847 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
837 848 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
838 849
839 850 users_group = relationship('UsersGroup')
840 851 permission = relationship('Permission')
841 852 repository = relationship('Repository')
842 853
843 854 @classmethod
844 855 def create(cls, users_group, repository, permission):
845 856 n = cls()
846 857 n.users_group = users_group
847 858 n.repository = repository
848 859 n.permission = permission
849 860 Session.add(n)
850 861 return n
851 862
852 863 def __repr__(self):
853 864 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
854 865
855 866 class UsersGroupToPerm(Base, BaseModel):
856 867 __tablename__ = 'users_group_to_perm'
857 868 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
858 869 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
859 870 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
860 871
861 872 users_group = relationship('UsersGroup')
862 873 permission = relationship('Permission')
863 874
864 875
865 876 class UserRepoGroupToPerm(Base, BaseModel):
866 877 __tablename__ = 'group_to_perm'
867 878 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
868 879
869 880 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
870 881 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
871 882 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
872 883 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
873 884
874 885 user = relationship('User')
875 886 permission = relationship('Permission')
876 887 group = relationship('RepoGroup')
877 888
878 889 class UsersGroupRepoGroupToPerm(Base, BaseModel):
879 890 __tablename__ = 'users_group_repo_group_to_perm'
880 891 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
881 892
882 893 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)
883 894 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
884 895 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
885 896 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
886 897
887 898 users_group = relationship('UsersGroup')
888 899 permission = relationship('Permission')
889 900 group = relationship('RepoGroup')
890 901
891 902 class Statistics(Base, BaseModel):
892 903 __tablename__ = 'statistics'
893 904 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
894 905 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
895 906 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
896 907 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
897 908 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
898 909 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
899 910 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
900 911
901 912 repository = relationship('Repository', single_parent=True)
902 913
903 914 class UserFollowing(Base, BaseModel):
904 915 __tablename__ = 'user_followings'
905 916 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
906 917 UniqueConstraint('user_id', 'follows_user_id')
907 918 , {'extend_existing':True})
908 919
909 920 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
910 921 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
911 922 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
912 923 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
913 924 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
914 925
915 926 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
916 927
917 928 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
918 929 follows_repository = relationship('Repository', order_by='Repository.repo_name')
919 930
920 931
921 932 @classmethod
922 933 def get_repo_followers(cls, repo_id):
923 934 return cls.query().filter(cls.follows_repo_id == repo_id)
924 935
925 936 class CacheInvalidation(Base, BaseModel):
926 937 __tablename__ = 'cache_invalidation'
927 938 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
928 939 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
929 940 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
930 941 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
931 942 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
932 943
933 944
934 945 def __init__(self, cache_key, cache_args=''):
935 946 self.cache_key = cache_key
936 947 self.cache_args = cache_args
937 948 self.cache_active = False
938 949
939 950 def __repr__(self):
940 951 return "<%s('%s:%s')>" % (self.__class__.__name__,
941 952 self.cache_id, self.cache_key)
942 953
943 954 @classmethod
944 955 def invalidate(cls, key):
945 956 """
946 957 Returns Invalidation object if this given key should be invalidated
947 958 None otherwise. `cache_active = False` means that this cache
948 959 state is not valid and needs to be invalidated
949 960
950 961 :param key:
951 962 """
952 963 return cls.query()\
953 964 .filter(CacheInvalidation.cache_key == key)\
954 965 .filter(CacheInvalidation.cache_active == False)\
955 966 .scalar()
956 967
957 968 @classmethod
958 969 def set_invalidate(cls, key):
959 970 """
960 971 Mark this Cache key for invalidation
961 972
962 973 :param key:
963 974 """
964 975
965 976 log.debug('marking %s for invalidation' % key)
966 977 inv_obj = Session.query(cls)\
967 978 .filter(cls.cache_key == key).scalar()
968 979 if inv_obj:
969 980 inv_obj.cache_active = False
970 981 else:
971 982 log.debug('cache key not found in invalidation db -> creating one')
972 983 inv_obj = CacheInvalidation(key)
973 984
974 985 try:
975 986 Session.add(inv_obj)
976 987 Session.commit()
977 988 except Exception:
978 989 log.error(traceback.format_exc())
979 990 Session.rollback()
980 991
981 992 @classmethod
982 993 def set_valid(cls, key):
983 994 """
984 995 Mark this cache key as active and currently cached
985 996
986 997 :param key:
987 998 """
988 999 inv_obj = CacheInvalidation.query()\
989 1000 .filter(CacheInvalidation.cache_key == key).scalar()
990 1001 inv_obj.cache_active = True
991 1002 Session.add(inv_obj)
992 1003 Session.commit()
993 1004
994 1005
995 1006 class ChangesetComment(Base, BaseModel):
996 1007 __tablename__ = 'changeset_comments'
997 1008 __table_args__ = ({'extend_existing':True},)
998 1009 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
999 1010 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1000 1011 revision = Column('revision', String(40), nullable=False)
1001 1012 line_no = Column('line_no', Unicode(10), nullable=True)
1002 1013 f_path = Column('f_path', Unicode(1000), nullable=True)
1003 1014 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1004 1015 text = Column('text', Unicode(25000), nullable=False)
1005 1016 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1006 1017
1007 1018 author = relationship('User', lazy='joined')
1008 1019 repo = relationship('Repository')
1009 1020
1010 1021
1011 1022 @classmethod
1012 1023 def get_users(cls, revision):
1013 1024 """
1014 1025 Returns user associated with this changesetComment. ie those
1015 1026 who actually commented
1016 1027
1017 1028 :param cls:
1018 1029 :param revision:
1019 1030 """
1020 1031 return Session.query(User)\
1021 1032 .filter(cls.revision == revision)\
1022 1033 .join(ChangesetComment.author).all()
1023 1034
1024 1035
1025 1036 class Notification(Base, BaseModel):
1026 1037 __tablename__ = 'notifications'
1027 1038 __table_args__ = ({'extend_existing':True})
1028 1039
1029 1040 TYPE_CHANGESET_COMMENT = u'cs_comment'
1030 1041 TYPE_MESSAGE = u'message'
1031 1042 TYPE_MENTION = u'mention'
1032 1043 TYPE_REGISTRATION = u'registration'
1033 1044
1034 1045 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1035 1046 subject = Column('subject', Unicode(512), nullable=True)
1036 1047 body = Column('body', Unicode(50000), nullable=True)
1037 1048 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1038 1049 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1039 1050 type_ = Column('type', Unicode(256))
1040 1051
1041 1052 created_by_user = relationship('User')
1042 1053 notifications_to_users = relationship('UserNotification', lazy='joined',
1043 1054 cascade="all, delete, delete-orphan")
1044 1055
1045 1056 @property
1046 1057 def recipients(self):
1047 1058 return [x.user for x in UserNotification.query()\
1048 1059 .filter(UserNotification.notification == self).all()]
1049 1060
1050 1061 @classmethod
1051 1062 def create(cls, created_by, subject, body, recipients, type_=None):
1052 1063 if type_ is None:
1053 1064 type_ = Notification.TYPE_MESSAGE
1054 1065
1055 1066 notification = cls()
1056 1067 notification.created_by_user = created_by
1057 1068 notification.subject = subject
1058 1069 notification.body = body
1059 1070 notification.type_ = type_
1060 1071 notification.created_on = datetime.datetime.now()
1061 1072
1062 1073 for u in recipients:
1063 1074 assoc = UserNotification()
1064 1075 assoc.notification = notification
1065 1076 u.notifications.append(assoc)
1066 1077 Session.add(notification)
1067 1078 return notification
1068 1079
1069 1080 @property
1070 1081 def description(self):
1071 1082 from rhodecode.model.notification import NotificationModel
1072 1083 return NotificationModel().make_description(self)
1073 1084
1074 1085 class UserNotification(Base, BaseModel):
1075 1086 __tablename__ = 'user_to_notification'
1076 1087 __table_args__ = (UniqueConstraint('user_id', 'notification_id'),
1077 1088 {'extend_existing':True})
1078 1089 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), primary_key=True)
1079 1090 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1080 1091 read = Column('read', Boolean, default=False)
1081 1092 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1082 1093
1083 1094 user = relationship('User', lazy="joined")
1084 1095 notification = relationship('Notification', lazy="joined",
1085 1096 order_by=lambda:Notification.created_on.desc(),)
1086 1097
1087 1098 def mark_as_read(self):
1088 1099 self.read = True
1089 1100 Session.add(self)
1090 1101
1091 1102 class DbMigrateVersion(Base, BaseModel):
1092 1103 __tablename__ = 'db_migrate_version'
1093 1104 __table_args__ = {'extend_existing':True}
1094 1105 repository_id = Column('repository_id', String(250), primary_key=True)
1095 1106 repository_path = Column('repository_path', Text)
1096 1107 version = Column('version', Integer)
1097 1108
@@ -1,682 +1,682 b''
1 1 """ this is forms validation classes
2 2 http://formencode.org/module-formencode.validators.html
3 3 for list off all availible validators
4 4
5 5 we can create our own validators
6 6
7 7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 8 pre_validators [] These validators will be applied before the schema
9 9 chained_validators [] These validators will be applied after the schema
10 10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14 14
15 15
16 16 <name> = formencode.validators.<name of validator>
17 17 <name> must equal form name
18 18 list=[1,2,3,4,5]
19 19 for SELECT use formencode.All(OneOf(list), Int())
20 20
21 21 """
22 22 import os
23 23 import re
24 24 import logging
25 25 import traceback
26 26
27 27 import formencode
28 28 from formencode import All
29 29 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
30 30 Email, Bool, StringBoolean, Set
31 31
32 32 from pylons.i18n.translation import _
33 33 from webhelpers.pylonslib.secure_form import authentication_token
34 34
35 35 from rhodecode.config.routing import ADMIN_PREFIX
36 36 from rhodecode.lib.utils import repo_name_slug
37 37 from rhodecode.lib.auth import authenticate, get_crypt_password
38 38 from rhodecode.lib.exceptions import LdapImportError
39 39 from rhodecode.model.db import User, UsersGroup, RepoGroup, Repository
40 40 from rhodecode import BACKENDS
41 41
42 42 log = logging.getLogger(__name__)
43 43
44 44 #this is needed to translate the messages using _() in validators
45 45 class State_obj(object):
46 46 _ = staticmethod(_)
47 47
48 48 #==============================================================================
49 49 # VALIDATORS
50 50 #==============================================================================
51 51 class ValidAuthToken(formencode.validators.FancyValidator):
52 52 messages = {'invalid_token':_('Token mismatch')}
53 53
54 54 def validate_python(self, value, state):
55 55
56 56 if value != authentication_token():
57 57 raise formencode.Invalid(self.message('invalid_token', state,
58 58 search_number=value), value, state)
59 59
60 60 def ValidUsername(edit, old_data):
61 61 class _ValidUsername(formencode.validators.FancyValidator):
62 62
63 63 def validate_python(self, value, state):
64 64 if value in ['default', 'new_user']:
65 65 raise formencode.Invalid(_('Invalid username'), value, state)
66 66 #check if user is unique
67 67 old_un = None
68 68 if edit:
69 69 old_un = User.get(old_data.get('user_id')).username
70 70
71 71 if old_un != value or not edit:
72 72 if User.get_by_username(value, case_insensitive=True):
73 73 raise formencode.Invalid(_('This username already '
74 74 'exists') , value, state)
75 75
76 76 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
77 77 raise formencode.Invalid(_('Username may only contain '
78 78 'alphanumeric characters '
79 79 'underscores, periods or dashes '
80 80 'and must begin with alphanumeric '
81 81 'character'), value, state)
82 82
83 83 return _ValidUsername
84 84
85 85
86 86 def ValidUsersGroup(edit, old_data):
87 87
88 88 class _ValidUsersGroup(formencode.validators.FancyValidator):
89 89
90 90 def validate_python(self, value, state):
91 91 if value in ['default']:
92 92 raise formencode.Invalid(_('Invalid group name'), value, state)
93 93 #check if group is unique
94 94 old_ugname = None
95 95 if edit:
96 96 old_ugname = UsersGroup.get(
97 97 old_data.get('users_group_id')).users_group_name
98 98
99 99 if old_ugname != value or not edit:
100 100 if UsersGroup.get_by_group_name(value, cache=False,
101 101 case_insensitive=True):
102 102 raise formencode.Invalid(_('This users group '
103 103 'already exists') , value,
104 104 state)
105 105
106 106
107 107 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
108 108 raise formencode.Invalid(_('RepoGroup name may only contain '
109 109 'alphanumeric characters '
110 110 'underscores, periods or dashes '
111 111 'and must begin with alphanumeric '
112 112 'character'), value, state)
113 113
114 114 return _ValidUsersGroup
115 115
116 116
117 117 def ValidReposGroup(edit, old_data):
118 118 class _ValidReposGroup(formencode.validators.FancyValidator):
119 119
120 120 def validate_python(self, value, state):
121 121 # TODO WRITE VALIDATIONS
122 122 group_name = value.get('group_name')
123 123 group_parent_id = value.get('group_parent_id')
124 124
125 125 # slugify repo group just in case :)
126 126 slug = repo_name_slug(group_name)
127 127
128 128 # check for parent of self
129 129 parent_of_self = lambda:(old_data['group_id'] == int(group_parent_id)
130 130 if group_parent_id else False)
131 131 if edit and parent_of_self():
132 132 e_dict = {'group_parent_id':_('Cannot assign this group '
133 133 'as parent')}
134 134 raise formencode.Invalid('', value, state,
135 135 error_dict=e_dict)
136 136
137 137 old_gname = None
138 138 if edit:
139 139 old_gname = RepoGroup.get(old_data.get('group_id')).group_name
140 140
141 141 if old_gname != group_name or not edit:
142 142
143 143 # check filesystem
144 144 gr = RepoGroup.query().filter(RepoGroup.group_name == slug)\
145 145 .filter(RepoGroup.group_parent_id == group_parent_id).scalar()
146 146
147 147 if gr:
148 148 e_dict = {'group_name':_('This group already exists')}
149 149 raise formencode.Invalid('', value, state,
150 150 error_dict=e_dict)
151 151
152 152 return _ValidReposGroup
153 153
154 154 class ValidPassword(formencode.validators.FancyValidator):
155 155
156 156 def to_python(self, value, state):
157 157
158 158 if value:
159 159
160 160 if value.get('password'):
161 161 try:
162 162 value['password'] = get_crypt_password(value['password'])
163 163 except UnicodeEncodeError:
164 164 e_dict = {'password':_('Invalid characters in password')}
165 165 raise formencode.Invalid('', value, state, error_dict=e_dict)
166 166
167 167 if value.get('password_confirmation'):
168 168 try:
169 169 value['password_confirmation'] = \
170 170 get_crypt_password(value['password_confirmation'])
171 171 except UnicodeEncodeError:
172 172 e_dict = {'password_confirmation':_('Invalid characters in password')}
173 173 raise formencode.Invalid('', value, state, error_dict=e_dict)
174 174
175 175 if value.get('new_password'):
176 176 try:
177 177 value['new_password'] = \
178 178 get_crypt_password(value['new_password'])
179 179 except UnicodeEncodeError:
180 180 e_dict = {'new_password':_('Invalid characters in password')}
181 181 raise formencode.Invalid('', value, state, error_dict=e_dict)
182 182
183 183 return value
184 184
185 185 class ValidPasswordsMatch(formencode.validators.FancyValidator):
186 186
187 187 def validate_python(self, value, state):
188 188
189 189 pass_val = value.get('password') or value.get('new_password')
190 190 if pass_val != value['password_confirmation']:
191 191 e_dict = {'password_confirmation':
192 192 _('Passwords do not match')}
193 193 raise formencode.Invalid('', value, state, error_dict=e_dict)
194 194
195 195 class ValidAuth(formencode.validators.FancyValidator):
196 196 messages = {
197 197 'invalid_password':_('invalid password'),
198 198 'invalid_login':_('invalid user name'),
199 199 'disabled_account':_('Your account is disabled')
200 200 }
201 201
202 202 # error mapping
203 203 e_dict = {'username':messages['invalid_login'],
204 204 'password':messages['invalid_password']}
205 205 e_dict_disable = {'username':messages['disabled_account']}
206 206
207 207 def validate_python(self, value, state):
208 208 password = value['password']
209 209 username = value['username']
210 210 user = User.get_by_username(username)
211 211
212 212 if authenticate(username, password):
213 213 return value
214 214 else:
215 215 if user and user.active is False:
216 216 log.warning('user %s is disabled', username)
217 217 raise formencode.Invalid(self.message('disabled_account',
218 218 state=State_obj),
219 219 value, state,
220 220 error_dict=self.e_dict_disable)
221 221 else:
222 222 log.warning('user %s not authenticated', username)
223 223 raise formencode.Invalid(self.message('invalid_password',
224 224 state=State_obj), value, state,
225 225 error_dict=self.e_dict)
226 226
227 227 class ValidRepoUser(formencode.validators.FancyValidator):
228 228
229 229 def to_python(self, value, state):
230 230 try:
231 231 User.query().filter(User.active == True)\
232 232 .filter(User.username == value).one()
233 233 except Exception:
234 234 raise formencode.Invalid(_('This username is not valid'),
235 235 value, state)
236 236 return value
237 237
238 238 def ValidRepoName(edit, old_data):
239 239 class _ValidRepoName(formencode.validators.FancyValidator):
240 240 def to_python(self, value, state):
241 241
242 242 repo_name = value.get('repo_name')
243 243
244 244 slug = repo_name_slug(repo_name)
245 245 if slug in [ADMIN_PREFIX, '']:
246 246 e_dict = {'repo_name': _('This repository name is disallowed')}
247 247 raise formencode.Invalid('', value, state, error_dict=e_dict)
248 248
249 249
250 250 if value.get('repo_group'):
251 251 gr = RepoGroup.get(value.get('repo_group'))
252 252 group_path = gr.full_path
253 253 # value needs to be aware of group name in order to check
254 254 # db key This is an actual just the name to store in the
255 255 # database
256 256 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
257 257
258 258 else:
259 259 group_path = ''
260 260 repo_name_full = repo_name
261 261
262 262
263 263 value['repo_name_full'] = repo_name_full
264 264 rename = old_data.get('repo_name') != repo_name_full
265 265 create = not edit
266 266 if rename or create:
267 267
268 268 if group_path != '':
269 269 if Repository.get_by_repo_name(repo_name_full):
270 270 e_dict = {'repo_name':_('This repository already '
271 271 'exists in a group "%s"') %
272 272 gr.group_name}
273 273 raise formencode.Invalid('', value, state,
274 274 error_dict=e_dict)
275 275 elif RepoGroup.get_by_group_name(repo_name_full):
276 276 e_dict = {'repo_name':_('There is a group with this'
277 277 ' name already "%s"') %
278 278 repo_name_full}
279 279 raise formencode.Invalid('', value, state,
280 280 error_dict=e_dict)
281 281
282 282 elif Repository.get_by_repo_name(repo_name_full):
283 283 e_dict = {'repo_name':_('This repository '
284 284 'already exists')}
285 285 raise formencode.Invalid('', value, state,
286 286 error_dict=e_dict)
287 287
288 288 return value
289 289
290 290 return _ValidRepoName
291 291
292 292 def ValidForkName(*args, **kwargs):
293 293 return ValidRepoName(*args, **kwargs)
294 294
295 295
296 296 def SlugifyName():
297 297 class _SlugifyName(formencode.validators.FancyValidator):
298 298
299 299 def to_python(self, value, state):
300 300 return repo_name_slug(value)
301 301
302 302 return _SlugifyName
303 303
304 304 def ValidCloneUri():
305 305 from mercurial.httprepo import httprepository, httpsrepository
306 306 from rhodecode.lib.utils import make_ui
307 307
308 308 class _ValidCloneUri(formencode.validators.FancyValidator):
309 309
310 310 def to_python(self, value, state):
311 311 if not value:
312 312 pass
313 313 elif value.startswith('https'):
314 314 try:
315 315 httpsrepository(make_ui('db'), value).capabilities
316 316 except Exception, e:
317 317 log.error(traceback.format_exc())
318 318 raise formencode.Invalid(_('invalid clone url'), value,
319 319 state)
320 320 elif value.startswith('http'):
321 321 try:
322 322 httprepository(make_ui('db'), value).capabilities
323 323 except Exception, e:
324 324 log.error(traceback.format_exc())
325 325 raise formencode.Invalid(_('invalid clone url'), value,
326 326 state)
327 327 else:
328 328 raise formencode.Invalid(_('Invalid clone url, provide a '
329 329 'valid clone http\s url'), value,
330 330 state)
331 331 return value
332 332
333 333 return _ValidCloneUri
334 334
335 335 def ValidForkType(old_data):
336 336 class _ValidForkType(formencode.validators.FancyValidator):
337 337
338 338 def to_python(self, value, state):
339 339 if old_data['repo_type'] != value:
340 340 raise formencode.Invalid(_('Fork have to be the same '
341 341 'type as original'), value, state)
342 342
343 343 return value
344 344 return _ValidForkType
345 345
346 346 class ValidPerms(formencode.validators.FancyValidator):
347 347 messages = {'perm_new_member_name':_('This username or users group name'
348 348 ' is not valid')}
349 349
350 350 def to_python(self, value, state):
351 351 perms_update = []
352 352 perms_new = []
353 353 #build a list of permission to update and new permission to create
354 354 for k, v in value.items():
355 355 #means new added member to permissions
356 356 if k.startswith('perm_new_member'):
357 357 new_perm = value.get('perm_new_member', False)
358 358 new_member = value.get('perm_new_member_name', False)
359 359 new_type = value.get('perm_new_member_type')
360 360
361 361 if new_member and new_perm:
362 362 if (new_member, new_perm, new_type) not in perms_new:
363 363 perms_new.append((new_member, new_perm, new_type))
364 364 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
365 365 member = k[7:]
366 366 t = {'u':'user',
367 367 'g':'users_group'}[k[0]]
368 368 if member == 'default':
369 369 if value['private']:
370 370 #set none for default when updating to private repo
371 371 v = 'repository.none'
372 372 perms_update.append((member, v, t))
373 373
374 374 value['perms_updates'] = perms_update
375 375 value['perms_new'] = perms_new
376 376
377 377 #update permissions
378 378 for k, v, t in perms_new:
379 379 try:
380 380 if t is 'user':
381 381 self.user_db = User.query()\
382 382 .filter(User.active == True)\
383 383 .filter(User.username == k).one()
384 384 if t is 'users_group':
385 385 self.user_db = UsersGroup.query()\
386 386 .filter(UsersGroup.users_group_active == True)\
387 387 .filter(UsersGroup.users_group_name == k).one()
388 388
389 389 except Exception:
390 390 msg = self.message('perm_new_member_name',
391 391 state=State_obj)
392 392 raise formencode.Invalid(msg, value, state,
393 393 error_dict={'perm_new_member_name':msg})
394 394 return value
395 395
396 396 class ValidSettings(formencode.validators.FancyValidator):
397 397
398 398 def to_python(self, value, state):
399 399 #settings form can't edit user
400 400 if value.has_key('user'):
401 401 del['value']['user']
402 402
403 403 return value
404 404
405 405 class ValidPath(formencode.validators.FancyValidator):
406 406 def to_python(self, value, state):
407 407
408 408 if not os.path.isdir(value):
409 409 msg = _('This is not a valid path')
410 410 raise formencode.Invalid(msg, value, state,
411 411 error_dict={'paths_root_path':msg})
412 412 return value
413 413
414 414 def UniqSystemEmail(old_data):
415 415 class _UniqSystemEmail(formencode.validators.FancyValidator):
416 416 def to_python(self, value, state):
417 417 value = value.lower()
418 418 if old_data.get('email') != value:
419 user = User.query().filter(User.email == value).scalar()
419 user = User.get_by_email(value, case_insensitive=True)
420 420 if user:
421 421 raise formencode.Invalid(
422 422 _("This e-mail address is already taken"),
423 423 value, state)
424 424 return value
425 425
426 426 return _UniqSystemEmail
427 427
428 428 class ValidSystemEmail(formencode.validators.FancyValidator):
429 429 def to_python(self, value, state):
430 430 value = value.lower()
431 user = User.query().filter(User.email == value).scalar()
431 user = User.get_by_email(value, case_insensitive=True)
432 432 if user is None:
433 433 raise formencode.Invalid(_("This e-mail address doesn't exist.") ,
434 434 value, state)
435 435
436 436 return value
437 437
438 438 class LdapLibValidator(formencode.validators.FancyValidator):
439 439
440 440 def to_python(self, value, state):
441 441
442 442 try:
443 443 import ldap
444 444 except ImportError:
445 445 raise LdapImportError
446 446 return value
447 447
448 448 class AttrLoginValidator(formencode.validators.FancyValidator):
449 449
450 450 def to_python(self, value, state):
451 451
452 452 if not value or not isinstance(value, (str, unicode)):
453 453 raise formencode.Invalid(_("The LDAP Login attribute of the CN "
454 454 "must be specified - this is the name "
455 455 "of the attribute that is equivalent "
456 456 "to 'username'"),
457 457 value, state)
458 458
459 459 return value
460 460
461 461 #===============================================================================
462 462 # FORMS
463 463 #===============================================================================
464 464 class LoginForm(formencode.Schema):
465 465 allow_extra_fields = True
466 466 filter_extra_fields = True
467 467 username = UnicodeString(
468 468 strip=True,
469 469 min=1,
470 470 not_empty=True,
471 471 messages={
472 472 'empty':_('Please enter a login'),
473 473 'tooShort':_('Enter a value %(min)i characters long or more')}
474 474 )
475 475
476 476 password = UnicodeString(
477 477 strip=True,
478 478 min=3,
479 479 not_empty=True,
480 480 messages={
481 481 'empty':_('Please enter a password'),
482 482 'tooShort':_('Enter %(min)i characters or more')}
483 483 )
484 484
485 485 chained_validators = [ValidAuth]
486 486
487 487 def UserForm(edit=False, old_data={}):
488 488 class _UserForm(formencode.Schema):
489 489 allow_extra_fields = True
490 490 filter_extra_fields = True
491 491 username = All(UnicodeString(strip=True, min=1, not_empty=True),
492 492 ValidUsername(edit, old_data))
493 493 if edit:
494 494 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
495 495 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=False))
496 496 admin = StringBoolean(if_missing=False)
497 497 else:
498 498 password = All(UnicodeString(strip=True, min=6, not_empty=True))
499 499 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=False))
500 500
501 501 active = StringBoolean(if_missing=False)
502 502 name = UnicodeString(strip=True, min=1, not_empty=True)
503 503 lastname = UnicodeString(strip=True, min=1, not_empty=True)
504 504 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
505 505
506 506 chained_validators = [ValidPasswordsMatch, ValidPassword]
507 507
508 508 return _UserForm
509 509
510 510
511 511 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
512 512 class _UsersGroupForm(formencode.Schema):
513 513 allow_extra_fields = True
514 514 filter_extra_fields = True
515 515
516 516 users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
517 517 ValidUsersGroup(edit, old_data))
518 518
519 519 users_group_active = StringBoolean(if_missing=False)
520 520
521 521 if edit:
522 522 users_group_members = OneOf(available_members, hideList=False,
523 523 testValueList=True,
524 524 if_missing=None, not_empty=False)
525 525
526 526 return _UsersGroupForm
527 527
528 528 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
529 529 class _ReposGroupForm(formencode.Schema):
530 530 allow_extra_fields = True
531 531 filter_extra_fields = True
532 532
533 533 group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
534 534 SlugifyName())
535 535 group_description = UnicodeString(strip=True, min=1,
536 536 not_empty=True)
537 537 group_parent_id = OneOf(available_groups, hideList=False,
538 538 testValueList=True,
539 539 if_missing=None, not_empty=False)
540 540
541 541 chained_validators = [ValidReposGroup(edit, old_data)]
542 542
543 543 return _ReposGroupForm
544 544
545 545 def RegisterForm(edit=False, old_data={}):
546 546 class _RegisterForm(formencode.Schema):
547 547 allow_extra_fields = True
548 548 filter_extra_fields = True
549 549 username = All(ValidUsername(edit, old_data),
550 550 UnicodeString(strip=True, min=1, not_empty=True))
551 551 password = All(UnicodeString(strip=True, min=6, not_empty=True))
552 552 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
553 553 active = StringBoolean(if_missing=False)
554 554 name = UnicodeString(strip=True, min=1, not_empty=True)
555 555 lastname = UnicodeString(strip=True, min=1, not_empty=True)
556 556 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
557 557
558 558 chained_validators = [ValidPasswordsMatch, ValidPassword]
559 559
560 560 return _RegisterForm
561 561
562 562 def PasswordResetForm():
563 563 class _PasswordResetForm(formencode.Schema):
564 564 allow_extra_fields = True
565 565 filter_extra_fields = True
566 566 email = All(ValidSystemEmail(), Email(not_empty=True))
567 567 return _PasswordResetForm
568 568
569 569 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
570 570 repo_groups=[]):
571 571 class _RepoForm(formencode.Schema):
572 572 allow_extra_fields = True
573 573 filter_extra_fields = False
574 574 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
575 575 SlugifyName())
576 576 clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False),
577 577 ValidCloneUri()())
578 578 repo_group = OneOf(repo_groups, hideList=True)
579 579 repo_type = OneOf(supported_backends)
580 580 description = UnicodeString(strip=True, min=1, not_empty=True)
581 581 private = StringBoolean(if_missing=False)
582 582 enable_statistics = StringBoolean(if_missing=False)
583 583 enable_downloads = StringBoolean(if_missing=False)
584 584
585 585 if edit:
586 586 #this is repo owner
587 587 user = All(UnicodeString(not_empty=True), ValidRepoUser)
588 588
589 589 chained_validators = [ValidRepoName(edit, old_data), ValidPerms]
590 590 return _RepoForm
591 591
592 592 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
593 593 repo_groups=[]):
594 594 class _RepoForkForm(formencode.Schema):
595 595 allow_extra_fields = True
596 596 filter_extra_fields = False
597 597 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
598 598 SlugifyName())
599 599 repo_group = OneOf(repo_groups, hideList=True)
600 600 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
601 601 description = UnicodeString(strip=True, min=1, not_empty=True)
602 602 private = StringBoolean(if_missing=False)
603 603 copy_permissions = StringBoolean(if_missing=False)
604 604 update_after_clone = StringBoolean(if_missing=False)
605 605 fork_parent_id = UnicodeString()
606 606 chained_validators = [ValidForkName(edit, old_data)]
607 607
608 608 return _RepoForkForm
609 609
610 610 def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
611 611 repo_groups=[]):
612 612 class _RepoForm(formencode.Schema):
613 613 allow_extra_fields = True
614 614 filter_extra_fields = False
615 615 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
616 616 SlugifyName())
617 617 description = UnicodeString(strip=True, min=1, not_empty=True)
618 618 repo_group = OneOf(repo_groups, hideList=True)
619 619 private = StringBoolean(if_missing=False)
620 620
621 621 chained_validators = [ValidRepoName(edit, old_data), ValidPerms,
622 622 ValidSettings]
623 623 return _RepoForm
624 624
625 625
626 626 def ApplicationSettingsForm():
627 627 class _ApplicationSettingsForm(formencode.Schema):
628 628 allow_extra_fields = True
629 629 filter_extra_fields = False
630 630 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
631 631 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
632 632 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
633 633
634 634 return _ApplicationSettingsForm
635 635
636 636 def ApplicationUiSettingsForm():
637 637 class _ApplicationUiSettingsForm(formencode.Schema):
638 638 allow_extra_fields = True
639 639 filter_extra_fields = False
640 640 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
641 641 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
642 642 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
643 643 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
644 644 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
645 645 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
646 646
647 647 return _ApplicationUiSettingsForm
648 648
649 649 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
650 650 class _DefaultPermissionsForm(formencode.Schema):
651 651 allow_extra_fields = True
652 652 filter_extra_fields = True
653 653 overwrite_default = StringBoolean(if_missing=False)
654 654 anonymous = OneOf(['True', 'False'], if_missing=False)
655 655 default_perm = OneOf(perms_choices)
656 656 default_register = OneOf(register_choices)
657 657 default_create = OneOf(create_choices)
658 658
659 659 return _DefaultPermissionsForm
660 660
661 661
662 662 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices):
663 663 class _LdapSettingsForm(formencode.Schema):
664 664 allow_extra_fields = True
665 665 filter_extra_fields = True
666 666 pre_validators = [LdapLibValidator]
667 667 ldap_active = StringBoolean(if_missing=False)
668 668 ldap_host = UnicodeString(strip=True,)
669 669 ldap_port = Number(strip=True,)
670 670 ldap_tls_kind = OneOf(tls_kind_choices)
671 671 ldap_tls_reqcert = OneOf(tls_reqcert_choices)
672 672 ldap_dn_user = UnicodeString(strip=True,)
673 673 ldap_dn_pass = UnicodeString(strip=True,)
674 674 ldap_base_dn = UnicodeString(strip=True,)
675 675 ldap_filter = UnicodeString(strip=True,)
676 676 ldap_search_scope = OneOf(search_scope_choices)
677 677 ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
678 678 ldap_attr_firstname = UnicodeString(strip=True,)
679 679 ldap_attr_lastname = UnicodeString(strip=True,)
680 680 ldap_attr_email = UnicodeString(strip=True,)
681 681
682 682 return _LdapSettingsForm
General Comments 0
You need to be logged in to leave comments. Login now