##// END OF EJS Templates
auth/security: enforce that external users cannot reset their password.
marcink -
r3258:e5497c9f default
parent child Browse files
Show More
@@ -185,7 +185,7 b' class AdminUsersView(BaseAppView, DataGr'
185 def users_new(self):
185 def users_new(self):
186 _ = self.request.translate
186 _ = self.request.translate
187 c = self.load_default_context()
187 c = self.load_default_context()
188 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
188 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
189 self._set_personal_repo_group_template_vars(c)
189 self._set_personal_repo_group_template_vars(c)
190 return self._get_template_context(c)
190 return self._get_template_context(c)
191
191
@@ -198,7 +198,7 b' class AdminUsersView(BaseAppView, DataGr'
198 def users_create(self):
198 def users_create(self):
199 _ = self.request.translate
199 _ = self.request.translate
200 c = self.load_default_context()
200 c = self.load_default_context()
201 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
201 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
202 user_model = UserModel()
202 user_model = UserModel()
203 user_form = UserForm(self.request.translate)()
203 user_form = UserForm(self.request.translate)()
204 try:
204 try:
@@ -32,6 +32,7 b' from pyramid.view import view_config'
32
32
33 from rhodecode.apps._base import BaseAppView
33 from rhodecode.apps._base import BaseAppView
34 from rhodecode.authentication.base import authenticate, HTTP_TYPE
34 from rhodecode.authentication.base import authenticate, HTTP_TYPE
35 from rhodecode.authentication.plugins import auth_rhodecode
35 from rhodecode.events import UserRegistered, trigger
36 from rhodecode.events import UserRegistered, trigger
36 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib import audit_logger
38 from rhodecode.lib import audit_logger
@@ -367,15 +368,24 b' class LoginView(BaseAppView):'
367 # matching emails
368 # matching emails
368 msg = _('If such email exists, a password reset link was sent to it.')
369 msg = _('If such email exists, a password reset link was sent to it.')
369
370
371 def default_response():
372 log.debug('faking response on invalid password reset')
373 # make this take 2s, to prevent brute forcing.
374 time.sleep(2)
375 h.flash(msg, category='success')
376 return HTTPFound(self.request.route_path('reset_password'))
377
370 if self.request.POST:
378 if self.request.POST:
371 if h.HasPermissionAny('hg.password_reset.disabled')():
379 if h.HasPermissionAny('hg.password_reset.disabled')():
372 _email = self.request.POST.get('email', '')
380 _email = self.request.POST.get('email', '')
373 log.error('Failed attempt to reset password for `%s`.', _email)
381 log.error('Failed attempt to reset password for `%s`.', _email)
374 h.flash(_('Password reset has been disabled.'),
382 h.flash(_('Password reset has been disabled.'), category='error')
375 category='error')
376 return HTTPFound(self.request.route_path('reset_password'))
383 return HTTPFound(self.request.route_path('reset_password'))
377
384
378 password_reset_form = PasswordResetForm(self.request.translate)()
385 password_reset_form = PasswordResetForm(self.request.translate)()
386 description = u'Generated token for password reset from {}'.format(
387 datetime.datetime.now().isoformat())
388
379 try:
389 try:
380 form_result = password_reset_form.to_python(
390 form_result = password_reset_form.to_python(
381 self.request.POST)
391 self.request.POST)
@@ -395,10 +405,14 b' class LoginView(BaseAppView):'
395 # Generate reset URL and send mail.
405 # Generate reset URL and send mail.
396 user = User.get_by_email(user_email)
406 user = User.get_by_email(user_email)
397
407
408 # only allow rhodecode based users to reset their password
409 # external auth shouldn't allow password reset
410 if user and user.extern_type != auth_rhodecode.RhodeCodeAuthPlugin.uid:
411 log.warning('User %s with external type `%s` tried a password reset. '
412 'This try was rejected', user, user.extern_type)
413 return default_response()
414
398 # generate password reset token that expires in 10 minutes
415 # generate password reset token that expires in 10 minutes
399 description = u'Generated token for password reset from {}'.format(
400 datetime.datetime.now().isoformat())
401
402 reset_token = UserModel().add_auth_token(
416 reset_token = UserModel().add_auth_token(
403 user=user, lifetime_minutes=10,
417 user=user, lifetime_minutes=10,
404 role=UserModel.auth_token_role.ROLE_PASSWORD_RESET,
418 role=UserModel.auth_token_role.ROLE_PASSWORD_RESET,
@@ -411,15 +425,14 b' class LoginView(BaseAppView):'
411 _query={'key': reset_token.api_key})
425 _query={'key': reset_token.api_key})
412 UserModel().reset_password_link(
426 UserModel().reset_password_link(
413 form_result, password_reset_url)
427 form_result, password_reset_url)
414 # Display success message and redirect.
415 h.flash(msg, category='success')
416
428
417 action_data = {'email': user_email,
429 action_data = {'email': user_email,
418 'user_agent': self.request.user_agent}
430 'user_agent': self.request.user_agent}
419 audit_logger.store_web(
431 audit_logger.store_web(
420 'user.password.reset_request', action_data=action_data,
432 'user.password.reset_request', action_data=action_data,
421 user=self._rhodecode_user, commit=True)
433 user=self._rhodecode_user, commit=True)
422 return HTTPFound(self.request.route_path('reset_password'))
434
435 return default_response()
423
436
424 except formencode.Invalid as errors:
437 except formencode.Invalid as errors:
425 template_context.update({
438 template_context.update({
@@ -434,11 +447,7 b' class LoginView(BaseAppView):'
434 # case of failed captcha
447 # case of failed captcha
435 return self._get_template_context(c, **template_context)
448 return self._get_template_context(c, **template_context)
436
449
437 log.debug('faking response on invalid password reset')
450 return default_response()
438 # make this take 2s, to prevent brute forcing.
439 time.sleep(2)
440 h.flash(msg, category='success')
441 return HTTPFound(self.request.route_path('reset_password'))
442
451
443 return self._get_template_context(c, **template_context)
452 return self._get_template_context(c, **template_context)
444
453
@@ -103,11 +103,12 b''
103 </div>
103 </div>
104 <div class="field">
104 <div class="field">
105 <div class="label-text">
105 <div class="label-text">
106 ${_('Source of Record')}:
106 ${_('Authentication type')}:
107 </div>
107 </div>
108 <div class="input">
108 <div class="input">
109 <p>${c.extern_type}</p>
109 <p>${c.extern_type}</p>
110 ${h.hidden('extern_type', readonly="readonly")}
110 ${h.hidden('extern_type', readonly="readonly")}
111 <p class="help-block">${_('User was created using an external source. He is bound to authentication using this method.')}</p>
111 </div>
112 </div>
112 </div>
113 </div>
113 <div class="field">
114 <div class="field">
@@ -127,7 +128,7 b''
127 ## allowed_languages is defined in the users.py
128 ## allowed_languages is defined in the users.py
128 ## c.language comes from base.py as a default language
129 ## c.language comes from base.py as a default language
129 ${h.select('language', c.language, c.allowed_languages)}
130 ${h.select('language', c.language, c.allowed_languages)}
130 <p class="help-block">${h.literal(_('Help translate %(rc_link)s into your language.') % {'rc_link': h.link_to('RhodeCode Enterprise', h.route_url('rhodecode_translations'))})}</p>
131 <p class="help-block">${h.literal(_('User interface language. Help translate %(rc_link)s into your language.') % {'rc_link': h.link_to('RhodeCode Enterprise', h.route_url('rhodecode_translations'))})}</p>
131 </div>
132 </div>
132 </div>
133 </div>
133 <div class="buttons">
134 <div class="buttons">
General Comments 0
You need to be logged in to leave comments. Login now