##// END OF EJS Templates
#344 optional firstname lastname on user creation...
marcink -
r1950:4ae17f81 beta
parent child Browse files
Show More
@@ -158,9 +158,9 b' INPUT::'
158 158 args : {
159 159 "username" : "<username>",
160 160 "password" : "<password>",
161 "firstname" : "<firstname>",
162 "lastname" : "<lastname>",
163 "email" : "<useremail>"
161 "email" : "<useremail>",
162 "firstname" : "<firstname> = None",
163 "lastname" : "<lastname> = None",
164 164 "active" : "<bool> = True",
165 165 "admin" : "<bool> = False",
166 166 "ldap_dn" : "<ldap_dn> = None"
@@ -35,6 +35,7 b' news'
35 35 based on user defined regular expression
36 36 - added linking of changesets in commit messages
37 37 - new compact changelog with expandable commit messages
38 - firstname and lastname are optional in user creation
38 39
39 40 fixes
40 41 -----
@@ -131,17 +131,17 b' class ApiController(JSONRPCController):'
131 131 return result
132 132
133 133 @HasPermissionAllDecorator('hg.admin')
134 def create_user(self, apiuser, username, password, firstname,
135 lastname, email, active=True, admin=False, ldap_dn=None):
134 def create_user(self, apiuser, username, password, email, firstname=None,
135 lastname=None, active=True, admin=False, ldap_dn=None):
136 136 """
137 137 Create new user or updates current one
138 138
139 139 :param apiuser:
140 140 :param username:
141 141 :param password:
142 :param email:
142 143 :param name:
143 144 :param lastname:
144 :param email:
145 145 :param active:
146 146 :param admin:
147 147 :param ldap_dn:
@@ -129,6 +129,7 b' def get_crypt_password(password):'
129 129 def check_password(password, hashed):
130 130 return RhodeCodeCrypto.hash_check(password, hashed)
131 131
132
132 133 def generate_api_key(str_, salt=None):
133 134 """
134 135 Generates API KEY from given string
@@ -237,6 +238,7 b' def authenticate(username, password):'
237 238 pass
238 239 return False
239 240
241
240 242 def login_container_auth(username):
241 243 user = User.get_by_username(username)
242 244 if user is None:
@@ -260,6 +262,7 b' def login_container_auth(username):'
260 262 user.username)
261 263 return user
262 264
265
263 266 def get_container_username(environ, config):
264 267 username = None
265 268
@@ -278,6 +281,7 b' def get_container_username(environ, conf'
278 281
279 282 return username
280 283
284
281 285 class AuthUser(object):
282 286 """
283 287 A simple object that handles all attributes of user in RhodeCode
@@ -302,6 +306,7 b' class AuthUser(object):'
302 306 self.permissions = {}
303 307 self._api_key = api_key
304 308 self.propagate_data()
309 self._instance = None
305 310
306 311 def propagate_data(self):
307 312 user_model = UserModel()
@@ -350,10 +355,6 b' class AuthUser(object):'
350 355 def is_admin(self):
351 356 return self.admin
352 357
353 @property
354 def full_contact(self):
355 return '%s %s <%s>' % (self.name, self.lastname, self.email)
356
357 358 def __repr__(self):
358 359 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
359 360 self.is_authenticated)
@@ -363,9 +364,9 b' class AuthUser(object):'
363 364 self.is_authenticated = authenticated
364 365
365 366 def get_cookie_store(self):
366 return {'username':self.username,
367 return {'username': self.username,
367 368 'user_id': self.user_id,
368 'is_authenticated':self.is_authenticated}
369 'is_authenticated': self.is_authenticated}
369 370
370 371 @classmethod
371 372 def from_cookie_store(cls, cookie_store):
@@ -374,6 +375,7 b' class AuthUser(object):'
374 375 api_key = cookie_store.get('api_key')
375 376 return AuthUser(user_id, api_key, username)
376 377
378
377 379 def set_available_permissions(config):
378 380 """
379 381 This function will propagate pylons globals with all available defined
@@ -388,7 +390,7 b' def set_available_permissions(config):'
388 390 try:
389 391 sa = meta.Session
390 392 all_perms = sa.query(Permission).all()
391 except:
393 except Exception:
392 394 pass
393 395 finally:
394 396 meta.Session.remove()
@@ -299,6 +299,11 b' class User(Base, BaseModel):'
299 299 return '%s %s' % (self.name, self.lastname)
300 300
301 301 @property
302 def full_name_or_username(self):
303 return ('%s %s' % (self.name, self.lastname)
304 if (self.name and self.lastname) else self.username)
305
306 @property
302 307 def full_contact(self):
303 308 return '%s %s <%s>' % (self.name, self.lastname, self.email)
304 309
@@ -354,8 +359,13 b' class User(Base, BaseModel):'
354 359 log.debug('updated user %s lastlogin', self.username)
355 360
356 361 def __json__(self):
357 return dict(email=self.email,
358 full_name=self.full_name)
362 return dict(
363 email=self.email,
364 full_name=self.full_name,
365 full_name_or_username=self.full_name_or_username,
366 short_contact=self.short_contact,
367 full_contact=self.full_contact
368 )
359 369
360 370
361 371 class UserLog(Base, BaseModel):
@@ -51,13 +51,17 b' class State_obj(object):'
51 51 # VALIDATORS
52 52 #==============================================================================
53 53 class ValidAuthToken(formencode.validators.FancyValidator):
54 messages = {'invalid_token':_('Token mismatch')}
54 messages = {'invalid_token': _('Token mismatch')}
55 55
56 56 def validate_python(self, value, state):
57 57
58 58 if value != authentication_token():
59 raise formencode.Invalid(self.message('invalid_token', state,
60 search_number=value), value, state)
59 raise formencode.Invalid(
60 self.message('invalid_token',
61 state, search_number=value),
62 value,
63 state
64 )
61 65
62 66
63 67 def ValidUsername(edit, old_data):
@@ -77,11 +81,13 b' def ValidUsername(edit, old_data):'
77 81 'exists') , value, state)
78 82
79 83 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
80 raise formencode.Invalid(_('Username may only contain '
81 'alphanumeric characters '
82 'underscores, periods or dashes '
83 'and must begin with alphanumeric '
84 'character'), value, state)
84 raise formencode.Invalid(
85 _('Username may only contain alphanumeric characters '
86 'underscores, periods or dashes and must begin with '
87 'alphanumeric character'),
88 value,
89 state
90 )
85 91
86 92 return _ValidUsername
87 93
@@ -103,15 +109,17 b' def ValidUsersGroup(edit, old_data):'
103 109 if UsersGroup.get_by_group_name(value, cache=False,
104 110 case_insensitive=True):
105 111 raise formencode.Invalid(_('This users group '
106 'already exists') , value,
112 'already exists'), value,
107 113 state)
108 114
109 115 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
110 raise formencode.Invalid(_('RepoGroup name may only contain '
111 'alphanumeric characters '
112 'underscores, periods or dashes '
113 'and must begin with alphanumeric '
114 'character'), value, state)
116 raise formencode.Invalid(
117 _('RepoGroup name may only contain alphanumeric characters '
118 'underscores, periods or dashes and must begin with '
119 'alphanumeric character'),
120 value,
121 state
122 )
115 123
116 124 return _ValidUsersGroup
117 125
@@ -177,32 +185,35 b' class ValidPassword(formencode.validator'
177 185
178 186 def to_python(self, value, state):
179 187
180 if value:
188 if not value:
189 return
181 190
182 if value.get('password'):
183 try:
184 value['password'] = get_crypt_password(value['password'])
185 except UnicodeEncodeError:
186 e_dict = {'password':_('Invalid characters in password')}
187 raise formencode.Invalid('', value, state, error_dict=e_dict)
191 if value.get('password'):
192 try:
193 value['password'] = get_crypt_password(value['password'])
194 except UnicodeEncodeError:
195 e_dict = {'password': _('Invalid characters in password')}
196 raise formencode.Invalid('', value, state, error_dict=e_dict)
188 197
189 if value.get('password_confirmation'):
190 try:
191 value['password_confirmation'] = \
192 get_crypt_password(value['password_confirmation'])
193 except UnicodeEncodeError:
194 e_dict = {'password_confirmation':_('Invalid characters in password')}
195 raise formencode.Invalid('', value, state, error_dict=e_dict)
198 if value.get('password_confirmation'):
199 try:
200 value['password_confirmation'] = \
201 get_crypt_password(value['password_confirmation'])
202 except UnicodeEncodeError:
203 e_dict = {
204 'password_confirmation': _('Invalid characters in password')
205 }
206 raise formencode.Invalid('', value, state, error_dict=e_dict)
196 207
197 if value.get('new_password'):
198 try:
199 value['new_password'] = \
200 get_crypt_password(value['new_password'])
201 except UnicodeEncodeError:
202 e_dict = {'new_password':_('Invalid characters in password')}
203 raise formencode.Invalid('', value, state, error_dict=e_dict)
208 if value.get('new_password'):
209 try:
210 value['new_password'] = \
211 get_crypt_password(value['new_password'])
212 except UnicodeEncodeError:
213 e_dict = {'new_password': _('Invalid characters in password')}
214 raise formencode.Invalid('', value, state, error_dict=e_dict)
204 215
205 return value
216 return value
206 217
207 218
208 219 class ValidPasswordsMatch(formencode.validators.FancyValidator):
@@ -224,9 +235,9 b' class ValidAuth(formencode.validators.Fa'
224 235 }
225 236
226 237 # error mapping
227 e_dict = {'username':messages['invalid_login'],
228 'password':messages['invalid_password']}
229 e_dict_disable = {'username':messages['disabled_account']}
238 e_dict = {'username': messages['invalid_login'],
239 'password': messages['invalid_password']}
240 e_dict_disable = {'username': messages['disabled_account']}
230 241
231 242 def validate_python(self, value, state):
232 243 password = value['password']
@@ -238,15 +249,19 b' class ValidAuth(formencode.validators.Fa'
238 249 else:
239 250 if user and user.active is False:
240 251 log.warning('user %s is disabled', username)
241 raise formencode.Invalid(self.message('disabled_account',
242 state=State_obj),
243 value, state,
244 error_dict=self.e_dict_disable)
252 raise formencode.Invalid(
253 self.message('disabled_account',
254 state=State_obj),
255 value, state,
256 error_dict=self.e_dict_disable
257 )
245 258 else:
246 259 log.warning('user %s not authenticated', username)
247 raise formencode.Invalid(self.message('invalid_password',
248 state=State_obj), value, state,
249 error_dict=self.e_dict)
260 raise formencode.Invalid(
261 self.message('invalid_password',
262 state=State_obj), value, state,
263 error_dict=self.e_dict
264 )
250 265
251 266
252 267 class ValidRepoUser(formencode.validators.FancyValidator):
@@ -272,7 +287,6 b' def ValidRepoName(edit, old_data):'
272 287 e_dict = {'repo_name': _('This repository name is disallowed')}
273 288 raise formencode.Invalid('', value, state, error_dict=e_dict)
274 289
275
276 290 if value.get('repo_group'):
277 291 gr = RepoGroup.get(value.get('repo_group'))
278 292 group_path = gr.full_path
@@ -285,7 +299,6 b' def ValidRepoName(edit, old_data):'
285 299 group_path = ''
286 300 repo_name_full = repo_name
287 301
288
289 302 value['repo_name_full'] = repo_name_full
290 303 rename = old_data.get('repo_name') != repo_name_full
291 304 create = not edit
@@ -293,20 +306,22 b' def ValidRepoName(edit, old_data):'
293 306
294 307 if group_path != '':
295 308 if Repository.get_by_repo_name(repo_name_full):
296 e_dict = {'repo_name':_('This repository already '
297 'exists in a group "%s"') %
298 gr.group_name}
309 e_dict = {
310 'repo_name': _('This repository already exists in '
311 'a group "%s"') % gr.group_name
312 }
299 313 raise formencode.Invalid('', value, state,
300 314 error_dict=e_dict)
301 315 elif RepoGroup.get_by_group_name(repo_name_full):
302 e_dict = {'repo_name':_('There is a group with this'
303 ' name already "%s"') %
304 repo_name_full}
316 e_dict = {
317 'repo_name': _('There is a group with this name '
318 'already "%s"') % repo_name_full
319 }
305 320 raise formencode.Invalid('', value, state,
306 321 error_dict=e_dict)
307 322
308 323 elif Repository.get_by_repo_name(repo_name_full):
309 e_dict = {'repo_name':_('This repository '
324 e_dict = {'repo_name': _('This repository '
310 325 'already exists')}
311 326 raise formencode.Invalid('', value, state,
312 327 error_dict=e_dict)
@@ -341,14 +356,14 b' def ValidCloneUri():'
341 356 elif value.startswith('https'):
342 357 try:
343 358 httpsrepository(make_ui('db'), value).capabilities
344 except Exception, e:
359 except Exception:
345 360 log.error(traceback.format_exc())
346 361 raise formencode.Invalid(_('invalid clone url'), value,
347 362 state)
348 363 elif value.startswith('http'):
349 364 try:
350 365 httprepository(make_ui('db'), value).capabilities
351 except Exception, e:
366 except Exception:
352 367 log.error(traceback.format_exc())
353 368 raise formencode.Invalid(_('invalid clone url'), value,
354 369 state)
@@ -374,7 +389,7 b' def ValidForkType(old_data):'
374 389
375 390
376 391 class ValidPerms(formencode.validators.FancyValidator):
377 messages = {'perm_new_member_name':_('This username or users group name'
392 messages = {'perm_new_member_name': _('This username or users group name'
378 393 ' is not valid')}
379 394
380 395 def to_python(self, value, state):
@@ -393,8 +408,9 b' class ValidPerms(formencode.validators.F'
393 408 perms_new.append((new_member, new_perm, new_type))
394 409 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
395 410 member = k[7:]
396 t = {'u':'user',
397 'g':'users_group'}[k[0]]
411 t = {'u': 'user',
412 'g': 'users_group'
413 }[k[0]]
398 414 if member == 'default':
399 415 if value['private']:
400 416 #set none for default when updating to private repo
@@ -419,8 +435,9 b' class ValidPerms(formencode.validators.F'
419 435 except Exception:
420 436 msg = self.message('perm_new_member_name',
421 437 state=State_obj)
422 raise formencode.Invalid(msg, value, state,
423 error_dict={'perm_new_member_name':msg})
438 raise formencode.Invalid(
439 msg, value, state, error_dict={'perm_new_member_name': msg}
440 )
424 441 return value
425 442
426 443
@@ -428,9 +445,8 b' class ValidSettings(formencode.validator'
428 445
429 446 def to_python(self, value, state):
430 447 # settings form can't edit user
431 if value.has_key('user'):
448 if 'user' in value:
432 449 del['value']['user']
433
434 450 return value
435 451
436 452
@@ -440,7 +456,7 b' class ValidPath(formencode.validators.Fa'
440 456 if not os.path.isdir(value):
441 457 msg = _('This is not a valid path')
442 458 raise formencode.Invalid(msg, value, state,
443 error_dict={'paths_root_path':msg})
459 error_dict={'paths_root_path': msg})
444 460 return value
445 461
446 462
@@ -448,12 +464,12 b' def UniqSystemEmail(old_data):'
448 464 class _UniqSystemEmail(formencode.validators.FancyValidator):
449 465 def to_python(self, value, state):
450 466 value = value.lower()
451 if old_data.get('email','').lower() != value:
467 if old_data.get('email', '').lower() != value:
452 468 user = User.get_by_email(value, case_insensitive=True)
453 469 if user:
454 470 raise formencode.Invalid(
455 _("This e-mail address is already taken"),
456 value, state)
471 _("This e-mail address is already taken"), value, state
472 )
457 473 return value
458 474
459 475 return _UniqSystemEmail
@@ -464,8 +480,9 b' class ValidSystemEmail(formencode.valida'
464 480 value = value.lower()
465 481 user = User.get_by_email(value, case_insensitive=True)
466 482 if user is None:
467 raise formencode.Invalid(_("This e-mail address doesn't exist.") ,
468 value, state)
483 raise formencode.Invalid(
484 _("This e-mail address doesn't exist."), value, state
485 )
469 486
470 487 return value
471 488
@@ -486,14 +503,15 b' class AttrLoginValidator(formencode.vali'
486 503 def to_python(self, value, state):
487 504
488 505 if not value or not isinstance(value, (str, unicode)):
489 raise formencode.Invalid(_("The LDAP Login attribute of the CN "
490 "must be specified - this is the name "
491 "of the attribute that is equivalent "
492 "to 'username'"),
493 value, state)
506 raise formencode.Invalid(
507 _("The LDAP Login attribute of the CN must be specified - "
508 "this is the name of the attribute that is equivalent "
509 "to 'username'"), value, state
510 )
494 511
495 512 return value
496 513
514
497 515 #==============================================================================
498 516 # FORMS
499 517 #==============================================================================
@@ -501,22 +519,22 b' class LoginForm(formencode.Schema):'
501 519 allow_extra_fields = True
502 520 filter_extra_fields = True
503 521 username = UnicodeString(
504 strip=True,
505 min=1,
506 not_empty=True,
507 messages={
508 'empty':_('Please enter a login'),
509 'tooShort':_('Enter a value %(min)i characters long or more')}
510 )
522 strip=True,
523 min=1,
524 not_empty=True,
525 messages={
526 'empty': _('Please enter a login'),
527 'tooShort': _('Enter a value %(min)i characters long or more')}
528 )
511 529
512 530 password = UnicodeString(
513 strip=True,
514 min=3,
515 not_empty=True,
516 messages={
517 'empty':_('Please enter a password'),
518 'tooShort':_('Enter %(min)i characters or more')}
519 )
531 strip=True,
532 min=3,
533 not_empty=True,
534 messages={
535 'empty': _('Please enter a password'),
536 'tooShort': _('Enter %(min)i characters or more')}
537 )
520 538
521 539 remember = StringBoolean(if_missing=False)
522 540
@@ -531,15 +549,17 b' def UserForm(edit=False, old_data={}):'
531 549 ValidUsername(edit, old_data))
532 550 if edit:
533 551 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
534 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=False))
552 password_confirmation = All(UnicodeString(strip=True, min=6,
553 not_empty=False))
535 554 admin = StringBoolean(if_missing=False)
536 555 else:
537 556 password = All(UnicodeString(strip=True, min=6, not_empty=True))
538 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=False))
557 password_confirmation = All(UnicodeString(strip=True, min=6,
558 not_empty=False))
539 559
540 560 active = StringBoolean(if_missing=False)
541 name = UnicodeString(strip=True, min=1, not_empty=True)
542 lastname = UnicodeString(strip=True, min=1, not_empty=True)
561 name = UnicodeString(strip=True, min=1, not_empty=False)
562 lastname = UnicodeString(strip=True, min=1, not_empty=False)
543 563 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
544 564
545 565 chained_validators = [ValidPasswordsMatch, ValidPassword]
@@ -592,14 +612,15 b' def RegisterForm(edit=False, old_data={}'
592 612 password = All(UnicodeString(strip=True, min=6, not_empty=True))
593 613 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
594 614 active = StringBoolean(if_missing=False)
595 name = UnicodeString(strip=True, min=1, not_empty=True)
596 lastname = UnicodeString(strip=True, min=1, not_empty=True)
615 name = UnicodeString(strip=True, min=1, not_empty=False)
616 lastname = UnicodeString(strip=True, min=1, not_empty=False)
597 617 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
598 618
599 619 chained_validators = [ValidPasswordsMatch, ValidPassword]
600 620
601 621 return _RegisterForm
602 622
623
603 624 def PasswordResetForm():
604 625 class _PasswordResetForm(formencode.Schema):
605 626 allow_extra_fields = True
@@ -607,6 +628,7 b' def PasswordResetForm():'
607 628 email = All(ValidSystemEmail(), Email(not_empty=True))
608 629 return _PasswordResetForm
609 630
631
610 632 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
611 633 repo_groups=[]):
612 634 class _RepoForm(formencode.Schema):
@@ -630,6 +652,7 b' def RepoForm(edit=False, old_data={}, su'
630 652 chained_validators = [ValidRepoName(edit, old_data), ValidPerms]
631 653 return _RepoForm
632 654
655
633 656 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
634 657 repo_groups=[]):
635 658 class _RepoForkForm(formencode.Schema):
@@ -648,6 +671,7 b' def RepoForkForm(edit=False, old_data={}'
648 671
649 672 return _RepoForkForm
650 673
674
651 675 def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
652 676 repo_groups=[]):
653 677 class _RepoForm(formencode.Schema):
@@ -674,6 +698,7 b' def ApplicationSettingsForm():'
674 698
675 699 return _ApplicationSettingsForm
676 700
701
677 702 def ApplicationUiSettingsForm():
678 703 class _ApplicationUiSettingsForm(formencode.Schema):
679 704 allow_extra_fields = True
@@ -687,6 +712,7 b' def ApplicationUiSettingsForm():'
687 712
688 713 return _ApplicationUiSettingsForm
689 714
715
690 716 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
691 717 class _DefaultPermissionsForm(formencode.Schema):
692 718 allow_extra_fields = True
@@ -92,7 +92,6 b' class UserModel(BaseModel):'
92 92 log.error(traceback.format_exc())
93 93 raise
94 94
95
96 95 def create_or_update(self, username, password, email, name, lastname,
97 96 active=True, admin=False, ldap_dn=None):
98 97 """
@@ -136,7 +135,6 b' class UserModel(BaseModel):'
136 135 log.error(traceback.format_exc())
137 136 raise
138 137
139
140 138 def create_for_container_auth(self, username, attrs):
141 139 """
142 140 Creates the given user if it's not already in the database
@@ -231,7 +229,7 b' class UserModel(BaseModel):'
231 229 body = body % (new_user.username, new_user.full_name,
232 230 new_user.email)
233 231 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
234 kw = {'registered_user_url':edit_url}
232 kw = {'registered_user_url': edit_url}
235 233 NotificationModel().create(created_by=new_user, subject=subject,
236 234 body=body, recipients=None,
237 235 type_=Notification.TYPE_REGISTRATION,
@@ -493,7 +491,6 b' class UserModel(BaseModel):'
493 491 new.permission = perm
494 492 self.sa.add(new)
495 493
496
497 494 def revoke_perm(self, user, perm):
498 495 if not isinstance(perm, Permission):
499 496 raise Exception('perm needs to be an instance of Permission class '
@@ -86,7 +86,7 b''
86 86 <label for="active">${_('Active')}:</label>
87 87 </div>
88 88 <div class="checkboxes">
89 ${h.checkbox('active',value=True)}
89 ${h.checkbox('active',value=True,checked='checked')}
90 90 </div>
91 91 </div>
92 92
@@ -112,7 +112,7 b''
112 112 ${h.end_form()}
113 113 %else:
114 114 <div class="links_left">
115 <div class="full_name">${c.rhodecode_user.full_name}</div>
115 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
116 116 <div class="email">${c.rhodecode_user.email}</div>
117 117 <div class="big_gravatar"><img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,48)}" /></div>
118 118 </div>
General Comments 0
You need to be logged in to leave comments. Login now