##// END OF EJS Templates
logging: improve how we log user lookup
marcink -
r76:f8fd77bc default
parent child Browse files
Show More
@@ -1,1076 +1,1075 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Set of generic validators
23 23 """
24 24
25 25 import logging
26 26 import os
27 27 import re
28 28 from collections import defaultdict
29 29
30 30 import formencode
31 31 import ipaddress
32 32 from formencode.validators import (
33 33 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set,
34 34 NotEmpty, IPAddress, CIDR, String, FancyValidator
35 35 )
36 36 from pylons.i18n.translation import _
37 37 from sqlalchemy.sql.expression import true
38 38 from sqlalchemy.util import OrderedSet
39 39 from webhelpers.pylonslib.secure_form import authentication_token
40 40
41 41 from rhodecode.config.routing import ADMIN_PREFIX
42 42 from rhodecode.lib.auth import HasRepoGroupPermissionAny, HasPermissionAny
43 43 from rhodecode.lib.exceptions import LdapImportError
44 44 from rhodecode.lib.utils import repo_name_slug, make_db_config
45 45 from rhodecode.lib.utils2 import safe_int, str2bool, aslist, md5
46 46 from rhodecode.lib.vcs.backends.git.repository import GitRepository
47 47 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
48 48 from rhodecode.lib.vcs.backends.svn.repository import SubversionRepository
49 49 from rhodecode.model.db import (
50 50 RepoGroup, Repository, UserGroup, User, ChangesetStatus, Gist)
51 51 from rhodecode.model.settings import VcsSettingsModel
52 52
53 53 # silence warnings and pylint
54 54 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set, \
55 55 NotEmpty, IPAddress, CIDR, String, FancyValidator
56 56
57 57 log = logging.getLogger(__name__)
58 58
59 59
60 60 class _Missing(object):
61 61 pass
62 62
63 63 Missing = _Missing()
64 64
65 65
66 66 class StateObj(object):
67 67 """
68 68 this is needed to translate the messages using _() in validators
69 69 """
70 70 _ = staticmethod(_)
71 71
72 72
73 73 def M(self, key, state=None, **kwargs):
74 74 """
75 75 returns string from self.message based on given key,
76 76 passed kw params are used to substitute %(named)s params inside
77 77 translated strings
78 78
79 79 :param msg:
80 80 :param state:
81 81 """
82 82 if state is None:
83 83 state = StateObj()
84 84 else:
85 85 state._ = staticmethod(_)
86 86 # inject validator into state object
87 87 return self.message(key, state, **kwargs)
88 88
89 89
90 90 def UniqueList(convert=None):
91 91 class _UniqueList(formencode.FancyValidator):
92 92 """
93 93 Unique List !
94 94 """
95 95 messages = {
96 96 'empty': _(u'Value cannot be an empty list'),
97 97 'missing_value': _(u'Value cannot be an empty list'),
98 98 }
99 99
100 100 def _to_python(self, value, state):
101 101 ret_val = []
102 102
103 103 def make_unique(value):
104 104 seen = []
105 105 return [c for c in value if not (c in seen or seen.append(c))]
106 106
107 107 if isinstance(value, list):
108 108 ret_val = make_unique(value)
109 109 elif isinstance(value, set):
110 110 ret_val = make_unique(list(value))
111 111 elif isinstance(value, tuple):
112 112 ret_val = make_unique(list(value))
113 113 elif value is None:
114 114 ret_val = []
115 115 else:
116 116 ret_val = [value]
117 117
118 118 if convert:
119 119 ret_val = map(convert, ret_val)
120 120 return ret_val
121 121
122 122 def empty_value(self, value):
123 123 return []
124 124
125 125 return _UniqueList
126 126
127 127
128 128 def UniqueListFromString():
129 129 class _UniqueListFromString(UniqueList()):
130 130 def _to_python(self, value, state):
131 131 if isinstance(value, basestring):
132 132 value = aslist(value, ',')
133 133 return super(_UniqueListFromString, self)._to_python(value, state)
134 134 return _UniqueListFromString
135 135
136 136
137 137 def ValidSvnPattern(section, repo_name=None):
138 138 class _validator(formencode.validators.FancyValidator):
139 139 messages = {
140 140 'pattern_exists': _(u'Pattern already exists'),
141 141 }
142 142
143 143 def validate_python(self, value, state):
144 144 if not value:
145 145 return
146 146 model = VcsSettingsModel(repo=repo_name)
147 147 ui_settings = model.get_svn_patterns(section=section)
148 148 for entry in ui_settings:
149 149 if value == entry.value:
150 150 msg = M(self, 'pattern_exists', state)
151 151 raise formencode.Invalid(msg, value, state)
152 152 return _validator
153 153
154 154
155 155 def ValidUsername(edit=False, old_data={}):
156 156 class _validator(formencode.validators.FancyValidator):
157 157 messages = {
158 158 'username_exists': _(u'Username "%(username)s" already exists'),
159 159 'system_invalid_username':
160 160 _(u'Username "%(username)s" is forbidden'),
161 161 'invalid_username':
162 162 _(u'Username may only contain alphanumeric characters '
163 163 u'underscores, periods or dashes and must begin with '
164 164 u'alphanumeric character or underscore')
165 165 }
166 166
167 167 def validate_python(self, value, state):
168 168 if value in ['default', 'new_user']:
169 169 msg = M(self, 'system_invalid_username', state, username=value)
170 170 raise formencode.Invalid(msg, value, state)
171 171 # check if user is unique
172 172 old_un = None
173 173 if edit:
174 174 old_un = User.get(old_data.get('user_id')).username
175 175
176 176 if old_un != value or not edit:
177 177 if User.get_by_username(value, case_insensitive=True):
178 178 msg = M(self, 'username_exists', state, username=value)
179 179 raise formencode.Invalid(msg, value, state)
180 180
181 181 if (re.match(r'^[\w]{1}[\w\-\.]{0,254}$', value)
182 182 is None):
183 183 msg = M(self, 'invalid_username', state)
184 184 raise formencode.Invalid(msg, value, state)
185 185 return _validator
186 186
187 187
188 188 def ValidRegex(msg=None):
189 189 class _validator(formencode.validators.Regex):
190 190 messages = {'invalid': msg or _(u'The input is not valid')}
191 191 return _validator
192 192
193 193
194 194 def ValidRepoUser():
195 195 class _validator(formencode.validators.FancyValidator):
196 196 messages = {
197 197 'invalid_username': _(u'Username %(username)s is not valid')
198 198 }
199 199
200 200 def validate_python(self, value, state):
201 201 try:
202 202 User.query().filter(User.active == true())\
203 203 .filter(User.username == value).one()
204 204 except Exception:
205 205 msg = M(self, 'invalid_username', state, username=value)
206 206 raise formencode.Invalid(
207 207 msg, value, state, error_dict={'username': msg}
208 208 )
209 209
210 210 return _validator
211 211
212 212
213 213 def ValidUserGroup(edit=False, old_data={}):
214 214 class _validator(formencode.validators.FancyValidator):
215 215 messages = {
216 216 'invalid_group': _(u'Invalid user group name'),
217 217 'group_exist': _(u'User group "%(usergroup)s" already exists'),
218 218 'invalid_usergroup_name':
219 219 _(u'user group name may only contain alphanumeric '
220 220 u'characters underscores, periods or dashes and must begin '
221 221 u'with alphanumeric character')
222 222 }
223 223
224 224 def validate_python(self, value, state):
225 225 if value in ['default']:
226 226 msg = M(self, 'invalid_group', state)
227 227 raise formencode.Invalid(
228 228 msg, value, state, error_dict={'users_group_name': msg}
229 229 )
230 230 # check if group is unique
231 231 old_ugname = None
232 232 if edit:
233 233 old_id = old_data.get('users_group_id')
234 234 old_ugname = UserGroup.get(old_id).users_group_name
235 235
236 236 if old_ugname != value or not edit:
237 237 is_existing_group = UserGroup.get_by_group_name(
238 238 value, case_insensitive=True)
239 239 if is_existing_group:
240 240 msg = M(self, 'group_exist', state, usergroup=value)
241 241 raise formencode.Invalid(
242 242 msg, value, state, error_dict={'users_group_name': msg}
243 243 )
244 244
245 245 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
246 246 msg = M(self, 'invalid_usergroup_name', state)
247 247 raise formencode.Invalid(
248 248 msg, value, state, error_dict={'users_group_name': msg}
249 249 )
250 250
251 251 return _validator
252 252
253 253
254 254 def ValidRepoGroup(edit=False, old_data={}, can_create_in_root=False):
255 255 class _validator(formencode.validators.FancyValidator):
256 256 messages = {
257 257 'group_parent_id': _(u'Cannot assign this group as parent'),
258 258 'group_exists': _(u'Group "%(group_name)s" already exists'),
259 259 'repo_exists': _(u'Repository with name "%(group_name)s" '
260 260 u'already exists'),
261 261 'permission_denied': _(u"no permission to store repository group"
262 262 u"in this location"),
263 263 'permission_denied_root': _(
264 264 u"no permission to store repository group "
265 265 u"in root location")
266 266 }
267 267
268 268 def _to_python(self, value, state):
269 269 group_name = repo_name_slug(value.get('group_name', ''))
270 270 group_parent_id = safe_int(value.get('group_parent_id'))
271 271 gr = RepoGroup.get(group_parent_id)
272 272 if gr:
273 273 parent_group_path = gr.full_path
274 274 # value needs to be aware of group name in order to check
275 275 # db key This is an actual just the name to store in the
276 276 # database
277 277 group_name_full = (
278 278 parent_group_path + RepoGroup.url_sep() + group_name)
279 279 else:
280 280 group_name_full = group_name
281 281
282 282 value['group_name'] = group_name
283 283 value['group_name_full'] = group_name_full
284 284 value['group_parent_id'] = group_parent_id
285 285 return value
286 286
287 287 def validate_python(self, value, state):
288 288
289 289 old_group_name = None
290 290 group_name = value.get('group_name')
291 291 group_name_full = value.get('group_name_full')
292 292 group_parent_id = safe_int(value.get('group_parent_id'))
293 293 if group_parent_id == -1:
294 294 group_parent_id = None
295 295
296 296 group_obj = RepoGroup.get(old_data.get('group_id'))
297 297 parent_group_changed = False
298 298 if edit:
299 299 old_group_name = group_obj.group_name
300 300 old_group_parent_id = group_obj.group_parent_id
301 301
302 302 if group_parent_id != old_group_parent_id:
303 303 parent_group_changed = True
304 304
305 305 # TODO: mikhail: the following if statement is not reached
306 306 # since group_parent_id's OneOf validation fails before.
307 307 # Can be removed.
308 308
309 309 # check against setting a parent of self
310 310 parent_of_self = (
311 311 old_data['group_id'] == group_parent_id
312 312 if group_parent_id else False
313 313 )
314 314 if parent_of_self:
315 315 msg = M(self, 'group_parent_id', state)
316 316 raise formencode.Invalid(
317 317 msg, value, state, error_dict={'group_parent_id': msg}
318 318 )
319 319
320 320 # group we're moving current group inside
321 321 child_group = None
322 322 if group_parent_id:
323 323 child_group = RepoGroup.query().filter(
324 324 RepoGroup.group_id == group_parent_id).scalar()
325 325
326 326 # do a special check that we cannot move a group to one of
327 327 # it's children
328 328 if edit and child_group:
329 329 parents = [x.group_id for x in child_group.parents]
330 330 move_to_children = old_data['group_id'] in parents
331 331 if move_to_children:
332 332 msg = M(self, 'group_parent_id', state)
333 333 raise formencode.Invalid(
334 334 msg, value, state, error_dict={'group_parent_id': msg})
335 335
336 336 # Check if we have permission to store in the parent.
337 337 # Only check if the parent group changed.
338 338 if parent_group_changed:
339 339 if child_group is None:
340 340 if not can_create_in_root:
341 341 msg = M(self, 'permission_denied_root', state)
342 342 raise formencode.Invalid(
343 343 msg, value, state,
344 344 error_dict={'group_parent_id': msg})
345 345 else:
346 346 valid = HasRepoGroupPermissionAny('group.admin')
347 347 forbidden = not valid(
348 348 child_group.group_name, 'can create group validator')
349 349 if forbidden:
350 350 msg = M(self, 'permission_denied', state)
351 351 raise formencode.Invalid(
352 352 msg, value, state,
353 353 error_dict={'group_parent_id': msg})
354 354
355 355 # if we change the name or it's new group, check for existing names
356 356 # or repositories with the same name
357 357 if old_group_name != group_name_full or not edit:
358 358 # check group
359 359 gr = RepoGroup.get_by_group_name(group_name_full)
360 360 if gr:
361 361 msg = M(self, 'group_exists', state, group_name=group_name)
362 362 raise formencode.Invalid(
363 363 msg, value, state, error_dict={'group_name': msg})
364 364
365 365 # check for same repo
366 366 repo = Repository.get_by_repo_name(group_name_full)
367 367 if repo:
368 368 msg = M(self, 'repo_exists', state, group_name=group_name)
369 369 raise formencode.Invalid(
370 370 msg, value, state, error_dict={'group_name': msg})
371 371
372 372 return _validator
373 373
374 374
375 375 def ValidPassword():
376 376 class _validator(formencode.validators.FancyValidator):
377 377 messages = {
378 378 'invalid_password':
379 379 _(u'Invalid characters (non-ascii) in password')
380 380 }
381 381
382 382 def validate_python(self, value, state):
383 383 try:
384 384 (value or '').decode('ascii')
385 385 except UnicodeError:
386 386 msg = M(self, 'invalid_password', state)
387 387 raise formencode.Invalid(msg, value, state,)
388 388 return _validator
389 389
390 390
391 391 def ValidOldPassword(username):
392 392 class _validator(formencode.validators.FancyValidator):
393 393 messages = {
394 394 'invalid_password': _(u'Invalid old password')
395 395 }
396 396
397 397 def validate_python(self, value, state):
398 398 from rhodecode.authentication.base import authenticate, HTTP_TYPE
399 399 if not authenticate(username, value, '', HTTP_TYPE):
400 400 msg = M(self, 'invalid_password', state)
401 401 raise formencode.Invalid(
402 402 msg, value, state, error_dict={'current_password': msg}
403 403 )
404 404 return _validator
405 405
406 406
407 407 def ValidPasswordsMatch(
408 408 passwd='new_password', passwd_confirmation='password_confirmation'):
409 409 class _validator(formencode.validators.FancyValidator):
410 410 messages = {
411 411 'password_mismatch': _(u'Passwords do not match'),
412 412 }
413 413
414 414 def validate_python(self, value, state):
415 415
416 416 pass_val = value.get('password') or value.get(passwd)
417 417 if pass_val != value[passwd_confirmation]:
418 418 msg = M(self, 'password_mismatch', state)
419 419 raise formencode.Invalid(
420 420 msg, value, state,
421 421 error_dict={passwd: msg, passwd_confirmation: msg}
422 422 )
423 423 return _validator
424 424
425 425
426 426 def ValidAuth():
427 427 class _validator(formencode.validators.FancyValidator):
428 428 messages = {
429 429 'invalid_password': _(u'invalid password'),
430 430 'invalid_username': _(u'invalid user name'),
431 431 'disabled_account': _(u'Your account is disabled')
432 432 }
433 433
434 434 def validate_python(self, value, state):
435 435 from rhodecode.authentication.base import authenticate, HTTP_TYPE
436 436
437 437 password = value['password']
438 438 username = value['username']
439 439
440 if not authenticate(username, password, '',
441 HTTP_TYPE,
440 if not authenticate(username, password, '', HTTP_TYPE,
442 441 skip_missing=True):
443 442 user = User.get_by_username(username)
444 443 if user and not user.active:
445 444 log.warning('user %s is disabled', username)
446 445 msg = M(self, 'disabled_account', state)
447 446 raise formencode.Invalid(
448 447 msg, value, state, error_dict={'username': msg}
449 448 )
450 449 else:
451 log.warning('user %s failed to authenticate', username)
450 log.warning('user `%s` failed to authenticate', username)
452 451 msg = M(self, 'invalid_username', state)
453 452 msg2 = M(self, 'invalid_password', state)
454 453 raise formencode.Invalid(
455 454 msg, value, state,
456 455 error_dict={'username': msg, 'password': msg2}
457 456 )
458 457 return _validator
459 458
460 459
461 460 def ValidAuthToken():
462 461 class _validator(formencode.validators.FancyValidator):
463 462 messages = {
464 463 'invalid_token': _(u'Token mismatch')
465 464 }
466 465
467 466 def validate_python(self, value, state):
468 467 if value != authentication_token():
469 468 msg = M(self, 'invalid_token', state)
470 469 raise formencode.Invalid(msg, value, state)
471 470 return _validator
472 471
473 472
474 473 def ValidRepoName(edit=False, old_data={}):
475 474 class _validator(formencode.validators.FancyValidator):
476 475 messages = {
477 476 'invalid_repo_name':
478 477 _(u'Repository name %(repo)s is disallowed'),
479 478 # top level
480 479 'repository_exists': _(u'Repository with name %(repo)s '
481 480 u'already exists'),
482 481 'group_exists': _(u'Repository group with name "%(repo)s" '
483 482 u'already exists'),
484 483 # inside a group
485 484 'repository_in_group_exists': _(u'Repository with name %(repo)s '
486 485 u'exists in group "%(group)s"'),
487 486 'group_in_group_exists': _(
488 487 u'Repository group with name "%(repo)s" '
489 488 u'exists in group "%(group)s"'),
490 489 }
491 490
492 491 def _to_python(self, value, state):
493 492 repo_name = repo_name_slug(value.get('repo_name', ''))
494 493 repo_group = value.get('repo_group')
495 494 if repo_group:
496 495 gr = RepoGroup.get(repo_group)
497 496 group_path = gr.full_path
498 497 group_name = gr.group_name
499 498 # value needs to be aware of group name in order to check
500 499 # db key This is an actual just the name to store in the
501 500 # database
502 501 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
503 502 else:
504 503 group_name = group_path = ''
505 504 repo_name_full = repo_name
506 505
507 506 value['repo_name'] = repo_name
508 507 value['repo_name_full'] = repo_name_full
509 508 value['group_path'] = group_path
510 509 value['group_name'] = group_name
511 510 return value
512 511
513 512 def validate_python(self, value, state):
514 513
515 514 repo_name = value.get('repo_name')
516 515 repo_name_full = value.get('repo_name_full')
517 516 group_path = value.get('group_path')
518 517 group_name = value.get('group_name')
519 518
520 519 if repo_name in [ADMIN_PREFIX, '']:
521 520 msg = M(self, 'invalid_repo_name', state, repo=repo_name)
522 521 raise formencode.Invalid(
523 522 msg, value, state, error_dict={'repo_name': msg})
524 523
525 524 rename = old_data.get('repo_name') != repo_name_full
526 525 create = not edit
527 526 if rename or create:
528 527
529 528 if group_path:
530 529 if Repository.get_by_repo_name(repo_name_full):
531 530 msg = M(self, 'repository_in_group_exists', state,
532 531 repo=repo_name, group=group_name)
533 532 raise formencode.Invalid(
534 533 msg, value, state, error_dict={'repo_name': msg})
535 534 if RepoGroup.get_by_group_name(repo_name_full):
536 535 msg = M(self, 'group_in_group_exists', state,
537 536 repo=repo_name, group=group_name)
538 537 raise formencode.Invalid(
539 538 msg, value, state, error_dict={'repo_name': msg})
540 539 else:
541 540 if RepoGroup.get_by_group_name(repo_name_full):
542 541 msg = M(self, 'group_exists', state, repo=repo_name)
543 542 raise formencode.Invalid(
544 543 msg, value, state, error_dict={'repo_name': msg})
545 544
546 545 if Repository.get_by_repo_name(repo_name_full):
547 546 msg = M(
548 547 self, 'repository_exists', state, repo=repo_name)
549 548 raise formencode.Invalid(
550 549 msg, value, state, error_dict={'repo_name': msg})
551 550 return value
552 551 return _validator
553 552
554 553
555 554 def ValidForkName(*args, **kwargs):
556 555 return ValidRepoName(*args, **kwargs)
557 556
558 557
559 558 def SlugifyName():
560 559 class _validator(formencode.validators.FancyValidator):
561 560
562 561 def _to_python(self, value, state):
563 562 return repo_name_slug(value)
564 563
565 564 def validate_python(self, value, state):
566 565 pass
567 566
568 567 return _validator
569 568
570 569
571 570 def ValidCloneUri():
572 571 class InvalidCloneUrl(Exception):
573 572 allowed_prefixes = ()
574 573
575 574 def url_handler(repo_type, url):
576 575 config = make_db_config(clear_session=False)
577 576 if repo_type == 'hg':
578 577 allowed_prefixes = ('http', 'svn+http', 'git+http')
579 578
580 579 if 'http' in url[:4]:
581 580 # initially check if it's at least the proper URL
582 581 # or does it pass basic auth
583 582 MercurialRepository.check_url(url, config)
584 583 elif 'svn+http' in url[:8]: # svn->hg import
585 584 SubversionRepository.check_url(url, config)
586 585 elif 'git+http' in url[:8]: # git->hg import
587 586 raise NotImplementedError()
588 587 else:
589 588 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
590 589 'Allowed url must start with one of %s'
591 590 % (url, ','.join(allowed_prefixes)))
592 591 exc.allowed_prefixes = allowed_prefixes
593 592 raise exc
594 593
595 594 elif repo_type == 'git':
596 595 allowed_prefixes = ('http', 'svn+http', 'hg+http')
597 596 if 'http' in url[:4]:
598 597 # initially check if it's at least the proper URL
599 598 # or does it pass basic auth
600 599 GitRepository.check_url(url, config)
601 600 elif 'svn+http' in url[:8]: # svn->git import
602 601 raise NotImplementedError()
603 602 elif 'hg+http' in url[:8]: # hg->git import
604 603 raise NotImplementedError()
605 604 else:
606 605 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
607 606 'Allowed url must start with one of %s'
608 607 % (url, ','.join(allowed_prefixes)))
609 608 exc.allowed_prefixes = allowed_prefixes
610 609 raise exc
611 610
612 611 class _validator(formencode.validators.FancyValidator):
613 612 messages = {
614 613 'clone_uri': _(u'invalid clone url for %(rtype)s repository'),
615 614 'invalid_clone_uri': _(
616 615 u'Invalid clone url, provide a valid clone '
617 616 u'url starting with one of %(allowed_prefixes)s')
618 617 }
619 618
620 619 def validate_python(self, value, state):
621 620 repo_type = value.get('repo_type')
622 621 url = value.get('clone_uri')
623 622
624 623 if url:
625 624 try:
626 625 url_handler(repo_type, url)
627 626 except InvalidCloneUrl as e:
628 627 log.warning(e)
629 628 msg = M(self, 'invalid_clone_uri', rtype=repo_type,
630 629 allowed_prefixes=','.join(e.allowed_prefixes))
631 630 raise formencode.Invalid(msg, value, state,
632 631 error_dict={'clone_uri': msg})
633 632 except Exception:
634 633 log.exception('Url validation failed')
635 634 msg = M(self, 'clone_uri', rtype=repo_type)
636 635 raise formencode.Invalid(msg, value, state,
637 636 error_dict={'clone_uri': msg})
638 637 return _validator
639 638
640 639
641 640 def ValidForkType(old_data={}):
642 641 class _validator(formencode.validators.FancyValidator):
643 642 messages = {
644 643 'invalid_fork_type': _(u'Fork have to be the same type as parent')
645 644 }
646 645
647 646 def validate_python(self, value, state):
648 647 if old_data['repo_type'] != value:
649 648 msg = M(self, 'invalid_fork_type', state)
650 649 raise formencode.Invalid(
651 650 msg, value, state, error_dict={'repo_type': msg}
652 651 )
653 652 return _validator
654 653
655 654
656 655 def CanWriteGroup(old_data=None):
657 656 class _validator(formencode.validators.FancyValidator):
658 657 messages = {
659 658 'permission_denied': _(
660 659 u"You do not have the permission "
661 660 u"to create repositories in this group."),
662 661 'permission_denied_root': _(
663 662 u"You do not have the permission to store repositories in "
664 663 u"the root location.")
665 664 }
666 665
667 666 def _to_python(self, value, state):
668 667 # root location
669 668 if value in [-1, "-1"]:
670 669 return None
671 670 return value
672 671
673 672 def validate_python(self, value, state):
674 673 gr = RepoGroup.get(value)
675 674 gr_name = gr.group_name if gr else None # None means ROOT location
676 675 # create repositories with write permission on group is set to true
677 676 create_on_write = HasPermissionAny(
678 677 'hg.create.write_on_repogroup.true')()
679 678 group_admin = HasRepoGroupPermissionAny('group.admin')(
680 679 gr_name, 'can write into group validator')
681 680 group_write = HasRepoGroupPermissionAny('group.write')(
682 681 gr_name, 'can write into group validator')
683 682 forbidden = not (group_admin or (group_write and create_on_write))
684 683 can_create_repos = HasPermissionAny(
685 684 'hg.admin', 'hg.create.repository')
686 685 gid = (old_data['repo_group'].get('group_id')
687 686 if (old_data and 'repo_group' in old_data) else None)
688 687 value_changed = gid != safe_int(value)
689 688 new = not old_data
690 689 # do check if we changed the value, there's a case that someone got
691 690 # revoked write permissions to a repository, he still created, we
692 691 # don't need to check permission if he didn't change the value of
693 692 # groups in form box
694 693 if value_changed or new:
695 694 # parent group need to be existing
696 695 if gr and forbidden:
697 696 msg = M(self, 'permission_denied', state)
698 697 raise formencode.Invalid(
699 698 msg, value, state, error_dict={'repo_type': msg}
700 699 )
701 700 # check if we can write to root location !
702 701 elif gr is None and not can_create_repos():
703 702 msg = M(self, 'permission_denied_root', state)
704 703 raise formencode.Invalid(
705 704 msg, value, state, error_dict={'repo_type': msg}
706 705 )
707 706
708 707 return _validator
709 708
710 709
711 710 def ValidPerms(type_='repo'):
712 711 if type_ == 'repo_group':
713 712 EMPTY_PERM = 'group.none'
714 713 elif type_ == 'repo':
715 714 EMPTY_PERM = 'repository.none'
716 715 elif type_ == 'user_group':
717 716 EMPTY_PERM = 'usergroup.none'
718 717
719 718 class _validator(formencode.validators.FancyValidator):
720 719 messages = {
721 720 'perm_new_member_name':
722 721 _(u'This username or user group name is not valid')
723 722 }
724 723
725 724 def _to_python(self, value, state):
726 725 perm_updates = OrderedSet()
727 726 perm_additions = OrderedSet()
728 727 perm_deletions = OrderedSet()
729 728 # build a list of permission to update/delete and new permission
730 729
731 730 # Read the perm_new_member/perm_del_member attributes and group
732 731 # them by they IDs
733 732 new_perms_group = defaultdict(dict)
734 733 del_perms_group = defaultdict(dict)
735 734 for k, v in value.copy().iteritems():
736 735 if k.startswith('perm_del_member'):
737 736 # delete from org storage so we don't process that later
738 737 del value[k]
739 738 # part is `id`, `type`
740 739 _type, part = k.split('perm_del_member_')
741 740 args = part.split('_')
742 741 if len(args) == 2:
743 742 _key, pos = args
744 743 del_perms_group[pos][_key] = v
745 744 if k.startswith('perm_new_member'):
746 745 # delete from org storage so we don't process that later
747 746 del value[k]
748 747 # part is `id`, `type`, `perm`
749 748 _type, part = k.split('perm_new_member_')
750 749 args = part.split('_')
751 750 if len(args) == 2:
752 751 _key, pos = args
753 752 new_perms_group[pos][_key] = v
754 753
755 754 # store the deletes
756 755 for k in sorted(del_perms_group.keys()):
757 756 perm_dict = del_perms_group[k]
758 757 del_member = perm_dict.get('id')
759 758 del_type = perm_dict.get('type')
760 759 if del_member and del_type:
761 760 perm_deletions.add((del_member, None, del_type))
762 761
763 762 # store additions in order of how they were added in web form
764 763 for k in sorted(new_perms_group.keys()):
765 764 perm_dict = new_perms_group[k]
766 765 new_member = perm_dict.get('id')
767 766 new_type = perm_dict.get('type')
768 767 new_perm = perm_dict.get('perm')
769 768 if new_member and new_perm and new_type:
770 769 perm_additions.add((new_member, new_perm, new_type))
771 770
772 771 # get updates of permissions
773 772 # (read the existing radio button states)
774 773 for k, update_value in value.iteritems():
775 774 if k.startswith('u_perm_') or k.startswith('g_perm_'):
776 775 member = k[7:]
777 776 update_type = {'u': 'user',
778 777 'g': 'users_group'}[k[0]]
779 778 if member == User.DEFAULT_USER:
780 779 if str2bool(value.get('repo_private')):
781 780 # set none for default when updating to
782 781 # private repo protects agains form manipulation
783 782 update_value = EMPTY_PERM
784 783 perm_updates.add((member, update_value, update_type))
785 784 # check the deletes
786 785
787 786 value['perm_additions'] = list(perm_additions)
788 787 value['perm_updates'] = list(perm_updates)
789 788 value['perm_deletions'] = list(perm_deletions)
790 789
791 790 # validate users they exist and they are active !
792 791 for member_id, _perm, member_type in perm_additions:
793 792 try:
794 793 if member_type == 'user':
795 794 self.user_db = User.query()\
796 795 .filter(User.active == true())\
797 796 .filter(User.user_id == member_id).one()
798 797 if member_type == 'users_group':
799 798 self.user_db = UserGroup.query()\
800 799 .filter(UserGroup.users_group_active == true())\
801 800 .filter(UserGroup.users_group_id == member_id)\
802 801 .one()
803 802
804 803 except Exception:
805 804 log.exception('Updated permission failed: org_exc:')
806 805 msg = M(self, 'perm_new_member_type', state)
807 806 raise formencode.Invalid(
808 807 msg, value, state, error_dict={
809 808 'perm_new_member_name': msg}
810 809 )
811 810 return value
812 811 return _validator
813 812
814 813
815 814 def ValidSettings():
816 815 class _validator(formencode.validators.FancyValidator):
817 816 def _to_python(self, value, state):
818 817 # settings form for users that are not admin
819 818 # can't edit certain parameters, it's extra backup if they mangle
820 819 # with forms
821 820
822 821 forbidden_params = [
823 822 'user', 'repo_type', 'repo_enable_locking',
824 823 'repo_enable_downloads', 'repo_enable_statistics'
825 824 ]
826 825
827 826 for param in forbidden_params:
828 827 if param in value:
829 828 del value[param]
830 829 return value
831 830
832 831 def validate_python(self, value, state):
833 832 pass
834 833 return _validator
835 834
836 835
837 836 def ValidPath():
838 837 class _validator(formencode.validators.FancyValidator):
839 838 messages = {
840 839 'invalid_path': _(u'This is not a valid path')
841 840 }
842 841
843 842 def validate_python(self, value, state):
844 843 if not os.path.isdir(value):
845 844 msg = M(self, 'invalid_path', state)
846 845 raise formencode.Invalid(
847 846 msg, value, state, error_dict={'paths_root_path': msg}
848 847 )
849 848 return _validator
850 849
851 850
852 851 def UniqSystemEmail(old_data={}):
853 852 class _validator(formencode.validators.FancyValidator):
854 853 messages = {
855 854 'email_taken': _(u'This e-mail address is already taken')
856 855 }
857 856
858 857 def _to_python(self, value, state):
859 858 return value.lower()
860 859
861 860 def validate_python(self, value, state):
862 861 if (old_data.get('email') or '').lower() != value:
863 862 user = User.get_by_email(value, case_insensitive=True)
864 863 if user:
865 864 msg = M(self, 'email_taken', state)
866 865 raise formencode.Invalid(
867 866 msg, value, state, error_dict={'email': msg}
868 867 )
869 868 return _validator
870 869
871 870
872 871 def ValidSystemEmail():
873 872 class _validator(formencode.validators.FancyValidator):
874 873 messages = {
875 874 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
876 875 }
877 876
878 877 def _to_python(self, value, state):
879 878 return value.lower()
880 879
881 880 def validate_python(self, value, state):
882 881 user = User.get_by_email(value, case_insensitive=True)
883 882 if user is None:
884 883 msg = M(self, 'non_existing_email', state, email=value)
885 884 raise formencode.Invalid(
886 885 msg, value, state, error_dict={'email': msg}
887 886 )
888 887
889 888 return _validator
890 889
891 890
892 891 def NotReviewedRevisions(repo_id):
893 892 class _validator(formencode.validators.FancyValidator):
894 893 messages = {
895 894 'rev_already_reviewed':
896 895 _(u'Revisions %(revs)s are already part of pull request '
897 896 u'or have set status'),
898 897 }
899 898
900 899 def validate_python(self, value, state):
901 900 # check revisions if they are not reviewed, or a part of another
902 901 # pull request
903 902 statuses = ChangesetStatus.query()\
904 903 .filter(ChangesetStatus.revision.in_(value))\
905 904 .filter(ChangesetStatus.repo_id == repo_id)\
906 905 .all()
907 906
908 907 errors = []
909 908 for status in statuses:
910 909 if status.pull_request_id:
911 910 errors.append(['pull_req', status.revision[:12]])
912 911 elif status.status:
913 912 errors.append(['status', status.revision[:12]])
914 913
915 914 if errors:
916 915 revs = ','.join([x[1] for x in errors])
917 916 msg = M(self, 'rev_already_reviewed', state, revs=revs)
918 917 raise formencode.Invalid(
919 918 msg, value, state, error_dict={'revisions': revs})
920 919
921 920 return _validator
922 921
923 922
924 923 def ValidIp():
925 924 class _validator(CIDR):
926 925 messages = {
927 926 'badFormat': _(u'Please enter a valid IPv4 or IpV6 address'),
928 927 'illegalBits': _(
929 928 u'The network size (bits) must be within the range '
930 929 u'of 0-32 (not %(bits)r)'),
931 930 }
932 931
933 932 # we ovveride the default to_python() call
934 933 def to_python(self, value, state):
935 934 v = super(_validator, self).to_python(value, state)
936 935 v = v.strip()
937 936 net = ipaddress.ip_network(address=v, strict=False)
938 937 return str(net)
939 938
940 939 def validate_python(self, value, state):
941 940 try:
942 941 addr = value.strip()
943 942 # this raises an ValueError if address is not IpV4 or IpV6
944 943 ipaddress.ip_network(addr, strict=False)
945 944 except ValueError:
946 945 raise formencode.Invalid(self.message('badFormat', state),
947 946 value, state)
948 947
949 948 return _validator
950 949
951 950
952 951 def FieldKey():
953 952 class _validator(formencode.validators.FancyValidator):
954 953 messages = {
955 954 'badFormat': _(
956 955 u'Key name can only consist of letters, '
957 956 u'underscore, dash or numbers'),
958 957 }
959 958
960 959 def validate_python(self, value, state):
961 960 if not re.match('[a-zA-Z0-9_-]+$', value):
962 961 raise formencode.Invalid(self.message('badFormat', state),
963 962 value, state)
964 963 return _validator
965 964
966 965
967 966 def BasePath():
968 967 class _validator(formencode.validators.FancyValidator):
969 968 messages = {
970 969 'badPath': _(u'Filename cannot be inside a directory'),
971 970 }
972 971
973 972 def _to_python(self, value, state):
974 973 return value
975 974
976 975 def validate_python(self, value, state):
977 976 if value != os.path.basename(value):
978 977 raise formencode.Invalid(self.message('badPath', state),
979 978 value, state)
980 979 return _validator
981 980
982 981
983 982 def ValidAuthPlugins():
984 983 class _validator(formencode.validators.FancyValidator):
985 984 messages = {
986 985 'import_duplicate': _(
987 986 u'Plugins %(loaded)s and %(next_to_load)s '
988 987 u'both export the same name'),
989 988 }
990 989
991 990 def _to_python(self, value, state):
992 991 # filter empty values
993 992 return filter(lambda s: s not in [None, ''], value)
994 993
995 994 def validate_python(self, value, state):
996 995 from rhodecode.authentication.base import loadplugin
997 996 module_list = value
998 997 unique_names = {}
999 998 try:
1000 999 for module in module_list:
1001 1000 plugin = loadplugin(module)
1002 1001 plugin_name = plugin.name
1003 1002 if plugin_name in unique_names:
1004 1003 msg = M(self, 'import_duplicate', state,
1005 1004 loaded=unique_names[plugin_name],
1006 1005 next_to_load=plugin_name)
1007 1006 raise formencode.Invalid(msg, value, state)
1008 1007 unique_names[plugin_name] = plugin
1009 1008 except (KeyError, AttributeError, TypeError) as e:
1010 1009 raise formencode.Invalid(str(e), value, state)
1011 1010
1012 1011 return _validator
1013 1012
1014 1013
1015 1014 def UniqGistId():
1016 1015 class _validator(formencode.validators.FancyValidator):
1017 1016 messages = {
1018 1017 'gistid_taken': _(u'This gistid is already in use')
1019 1018 }
1020 1019
1021 1020 def _to_python(self, value, state):
1022 1021 return repo_name_slug(value.lower())
1023 1022
1024 1023 def validate_python(self, value, state):
1025 1024 existing = Gist.get_by_access_id(value)
1026 1025 if existing:
1027 1026 msg = M(self, 'gistid_taken', state)
1028 1027 raise formencode.Invalid(
1029 1028 msg, value, state, error_dict={'gistid': msg}
1030 1029 )
1031 1030
1032 1031 return _validator
1033 1032
1034 1033
1035 1034 def ValidPattern():
1036 1035
1037 1036 class _Validator(formencode.validators.FancyValidator):
1038 1037
1039 1038 def _to_python(self, value, state):
1040 1039 patterns = []
1041 1040
1042 1041 prefix = 'new_pattern'
1043 1042 for name, v in value.iteritems():
1044 1043 pattern_name = '_'.join((prefix, 'pattern'))
1045 1044 if name.startswith(pattern_name):
1046 1045 new_item_id = name[len(pattern_name)+1:]
1047 1046
1048 1047 def _field(name):
1049 1048 return '%s_%s_%s' % (prefix, name, new_item_id)
1050 1049
1051 1050 values = {
1052 1051 'issuetracker_pat': value.get(_field('pattern')),
1053 1052 'issuetracker_pat': value.get(_field('pattern')),
1054 1053 'issuetracker_url': value.get(_field('url')),
1055 1054 'issuetracker_pref': value.get(_field('prefix')),
1056 1055 'issuetracker_desc': value.get(_field('description'))
1057 1056 }
1058 1057 new_uid = md5(values['issuetracker_pat'])
1059 1058
1060 1059 has_required_fields = (
1061 1060 values['issuetracker_pat']
1062 1061 and values['issuetracker_url'])
1063 1062
1064 1063 if has_required_fields:
1065 1064 settings = [
1066 1065 ('_'.join((key, new_uid)), values[key], 'unicode')
1067 1066 for key in values]
1068 1067 patterns.append(settings)
1069 1068
1070 1069 value['patterns'] = patterns
1071 1070 delete_patterns = value.get('uid') or []
1072 1071 if not isinstance(delete_patterns, (list, tuple)):
1073 1072 delete_patterns = [delete_patterns]
1074 1073 value['delete_patterns'] = delete_patterns
1075 1074 return value
1076 1075 return _Validator
General Comments 0
You need to be logged in to leave comments. Login now