changeset 1419:febb3f954509 beta

merge
author Marcin Kuzminski <marcin@python-works.com>
date Mon, 04 Jul 2011 18:07:49 +0200
parents df04752daa64 (current diff) b097c4e328a2 (diff)
children a2fe0ac8d007
files rhodecode/tests/__init__.py setup.cfg
diffstat 11 files changed, 172 insertions(+), 58 deletions(-) [+]
line wrap: on
line diff
--- a/rhodecode/config/routing.py	Sat Jul 02 20:38:54 2011 +0200
+++ b/rhodecode/config/routing.py	Mon Jul 04 18:07:49 2011 +0200
@@ -294,6 +294,10 @@
     rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
                  controller='login', action='password_reset')
 
+    rmap.connect('reset_password_confirmation',
+                 '%s/password_reset_confirmation' % ADMIN_PREFIX,
+                 controller='login', action='password_reset_confirmation')
+
     #FEEDS
     rmap.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
                 controller='feed', action='rss',
--- a/rhodecode/controllers/login.py	Sat Jul 02 20:38:54 2011 +0200
+++ b/rhodecode/controllers/login.py	Mon Jul 04 18:07:49 2011 +0200
@@ -129,8 +129,8 @@
             password_reset_form = PasswordResetForm()()
             try:
                 form_result = password_reset_form.to_python(dict(request.POST))
-                user_model.reset_password(form_result)
-                h.flash(_('Your new password was sent'),
+                user_model.reset_password_link(form_result)
+                h.flash(_('Your password reset link was sent'),
                             category='success')
                 return redirect(url('login_home'))
 
@@ -144,6 +144,23 @@
 
         return render('/password_reset.html')
 
+    def password_reset_confirmation(self):
+
+        if request.GET and request.GET.get('key'):
+            try:
+                user_model = UserModel()
+                user = User.get_by_api_key(request.GET.get('key'))
+                data = dict(email=user.email)
+                user_model.reset_password(data)
+                h.flash(_('Your password reset was successful, '
+                          'new password has been sent to your email'),
+                            category='success')
+            except Exception, e:
+                log.error(e)
+                return redirect(url('reset_password'))
+
+        return redirect(url('login_home'))
+
     def logout(self):
         del session['rhodecode_user']
         session.save()
--- a/rhodecode/lib/celerylib/tasks.py	Sat Jul 02 20:38:54 2011 +0200
+++ b/rhodecode/lib/celerylib/tasks.py	Mon Jul 04 18:07:49 2011 +0200
@@ -34,7 +34,7 @@
 from operator import itemgetter
 from string import lower
 
-from pylons import config
+from pylons import config, url
 from pylons.i18n.translation import _
 
 from rhodecode.lib import LANGUAGES_EXTENSIONS_MAP, safe_str
@@ -249,6 +249,45 @@
         log.info('LockHeld')
         return 'Task with key %s already running' % lockkey
 
+@task(ignore_result=True)
+def send_password_link(user_email):
+    try:
+        log = reset_user_password.get_logger()
+    except:
+        log = logging.getLogger(__name__)
+
+    from rhodecode.lib import auth
+    from rhodecode.model.db import User
+
+    try:
+        sa = get_session()
+        user = sa.query(User).filter(User.email == user_email).scalar()
+
+        if user:
+            link = url('reset_password_confirmation', key=user.api_key,
+                       qualified=True)
+            tmpl = """
+Hello %s
+
+We received a request to create a new password for your account.
+
+You can generate it by clicking following URL:
+
+%s
+
+If you didn't request new password please ignore this email.
+            """
+            run_task(send_email, user_email,
+                     "RhodeCode password reset link",
+                     tmpl % (user.short_contact, link))
+            log.info('send new password mail to %s', user_email)
+
+    except:
+        log.error('Failed to update user password')
+        log.error(traceback.format_exc())
+        return False
+
+    return True
 
 @task(ignore_result=True)
 def reset_user_password(user_email):
@@ -280,8 +319,8 @@
             sa.rollback()
 
         run_task(send_email, user_email,
-                 "Your new rhodecode password",
-                 'Your new rhodecode password:%s' % (new_passwd))
+                 "Your new RhodeCode password",
+                 'Your new RhodeCode password:%s' % (new_passwd))
         log.info('send new password mail to %s', user_email)
 
     except:
--- a/rhodecode/lib/smtp_mailer.py	Sat Jul 02 20:38:54 2011 +0200
+++ b/rhodecode/lib/smtp_mailer.py	Mon Jul 04 18:07:49 2011 +0200
@@ -74,13 +74,19 @@
 
         date_ = formatdate(localtime=True)
         msg = MIMEMultipart()
+        msg.set_type('multipart/alternative')
+        msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
+
+        text_msg = MIMEText(body)
+        text_msg.set_type('text/plain')
+        text_msg.set_param('charset', 'UTF-8')
+
         msg['From'] = self.mail_from
         msg['To'] = ','.join(recipients)
         msg['Date'] = date_
         msg['Subject'] = subject
-        msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
 
-        msg.attach(MIMEText(body))
+        msg.attach(text_msg)
 
         if attachment_files:
             self.__atach_files(msg, attachment_files)
--- a/rhodecode/model/db.py	Sat Jul 02 20:38:54 2011 +0200
+++ b/rhodecode/model/db.py	Mon Jul 04 18:07:49 2011 +0200
@@ -243,6 +243,11 @@
         else:
             return Session.query(cls).filter(cls.username == username).one()
 
+    @classmethod
+    def get_by_api_key(cls, api_key):
+        return Session.query(cls).filter(cls.api_key == api_key).one()
+
+
     def update_lastlogin(self):
         """Update user lastlogin"""
 
--- a/rhodecode/model/user.py	Sat Jul 02 20:38:54 2011 +0200
+++ b/rhodecode/model/user.py	Mon Jul 04 18:07:49 2011 +0200
@@ -213,6 +213,10 @@
             self.sa.rollback()
             raise
 
+    def reset_password_link(self, data):
+        from rhodecode.lib.celerylib import tasks, run_task
+        run_task(tasks.send_password_link, data['email'])
+
     def reset_password(self, data):
         from rhodecode.lib.celerylib import tasks, run_task
         run_task(tasks.reset_user_password, data['email'])
--- a/rhodecode/public/css/style.css	Sat Jul 02 20:38:54 2011 +0200
+++ b/rhodecode/public/css/style.css	Mon Jul 04 18:07:49 2011 +0200
@@ -198,7 +198,8 @@
 -moz-border-radius: 0px 0px 8px 8px;
 border-radius: 0px 0px 8px 8px;
 height:37px;
-background:url("../images/header_inner.png") repeat-x scroll 0 0 #003367
+background:url("../images/header_inner.png") repeat-x scroll 0 0 #003367;
+box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
 }
 
 #header ul#logged-user li {
@@ -1383,6 +1384,13 @@
 margin-left: -16px;
 width: 281px;
 border-radius: 0 0 8px 8px;
+box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
+}
+
+#quick_login .password_forgoten{
+padding-right:10px;
+padding-top:10px;
+float:left;
 }
 
 #quick_login div.form div.fields{
--- a/rhodecode/templates/base/base.html	Sat Jul 02 20:38:54 2011 +0200
+++ b/rhodecode/templates/base/base.html	Mon Jul 04 18:07:49 2011 +0200
@@ -30,7 +30,7 @@
 			            
 			        </div>
 			        <div class="buttons">
-			            ${h.submit('sign_in','Sign In',class_="ui-button")}
+			            <div class="password_forgoten">${h.link_to(_('Forgot password ?'),h.url('reset_password'))}</div>${h.submit('sign_in','Sign In',class_="ui-button")}
 			        </div>
 			    </div>
 			</div>
--- a/rhodecode/templates/password_reset.html	Sat Jul 02 20:38:54 2011 +0200
+++ b/rhodecode/templates/password_reset.html	Mon Jul 04 18:07:49 2011 +0200
@@ -28,7 +28,7 @@
 	            <div class="buttons">
 		            <div class="nohighlight">
 		              ${h.submit('send','Reset my password',class_="ui-button")}
-					  	<div class="activation_msg">${_('Your new password will be send to matching email address')}</div>
+					  	<div class="activation_msg">${_('Password reset link will be send to matching email address')}</div>
 		            </div>
 	            </div>             
 	    	</div>
--- a/rhodecode/tests/functional/test_login.py	Sat Jul 02 20:38:54 2011 +0200
+++ b/rhodecode/tests/functional/test_login.py	Mon Jul 04 18:07:49 2011 +0200
@@ -1,6 +1,7 @@
 # -*- coding: utf-8 -*-
 from rhodecode.tests import *
 from rhodecode.model.db import User
+from rhodecode.lib import generate_api_key
 from rhodecode.lib.auth import check_password
 
 
@@ -8,39 +9,42 @@
 
     def test_index(self):
         response = self.app.get(url(controller='login', action='index'))
-        assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
+        self.assertEqual(response.status, '200 OK')
         # Test response...
 
     def test_login_admin_ok(self):
         response = self.app.post(url(controller='login', action='index'),
                                  {'username':'test_admin',
                                   'password':'test12'})
-        assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
-        assert response.session['rhodecode_user'].username == 'test_admin', 'wrong logged in user'
+        self.assertEqual(response.status, '302 Found')
+        self.assertEqual(response.session['rhodecode_user'].username ,
+                         'test_admin')
         response = response.follow()
-        assert '%s repository' % HG_REPO in response.body
+        self.assertTrue('%s repository' % HG_REPO in response.body)
 
     def test_login_regular_ok(self):
         response = self.app.post(url(controller='login', action='index'),
                                  {'username':'test_regular',
                                   'password':'test12'})
-        print response
-        assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
-        assert response.session['rhodecode_user'].username == 'test_regular', 'wrong logged in user'
+
+        self.assertEqual(response.status, '302 Found')
+        self.assertEqual(response.session['rhodecode_user'].username ,
+                         'test_regular')
         response = response.follow()
-        assert '%s repository' % HG_REPO in response.body
-        assert '<a title="Admin" href="/_admin">' not in response.body
+        self.assertTrue('%s repository' % HG_REPO in response.body)
+        self.assertTrue('<a title="Admin" href="/_admin">' not in response.body)
 
     def test_login_ok_came_from(self):
         test_came_from = '/_admin/users'
-        response = self.app.post(url(controller='login', action='index', came_from=test_came_from),
+        response = self.app.post(url(controller='login', action='index',
+                                     came_from=test_came_from),
                                  {'username':'test_admin',
                                   'password':'test12'})
-        assert response.status == '302 Found', 'Wrong response code from came from redirection'
+        self.assertEqual(response.status, '302 Found')
         response = response.follow()
 
-        assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
-        assert 'Users administration' in response.body, 'No proper title in response'
+        self.assertEqual(response.status, '200 OK')
+        self.assertTrue('Users administration' in response.body)
 
 
     def test_login_short_password(self):
@@ -48,24 +52,24 @@
                                  {'username':'test_admin',
                                   'password':'as'})
         self.assertEqual(response.status, '200 OK')
-        print response.body
+
         self.assertTrue('Enter 3 characters or more' in response.body)
 
     def test_login_wrong_username_password(self):
         response = self.app.post(url(controller='login', action='index'),
                                  {'username':'error',
                                   'password':'test12'})
-        assert response.status == '200 OK', 'Wrong response from login page'
+        self.assertEqual(response.status , '200 OK')
 
-        assert 'invalid user name' in response.body, 'No error username message in response'
-        assert 'invalid password' in response.body, 'No error password message in response'
+        self.assertTrue('invalid user name' in response.body)
+        self.assertTrue('invalid password' in response.body)
 
     #==========================================================================
     # REGISTRATIONS
     #==========================================================================
     def test_register(self):
         response = self.app.get(url(controller='login', action='register'))
-        assert 'Sign Up to RhodeCode' in response.body, 'wrong page for user registration'
+        self.assertTrue('Sign Up to RhodeCode' in response.body)
 
     def test_register_err_same_username(self):
         response = self.app.post(url(controller='login', action='register'),
@@ -76,8 +80,8 @@
                                              'name':'test',
                                              'lastname':'test'})
 
-        assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
-        assert 'This username already exists' in response.body
+        self.assertEqual(response.status , '200 OK')
+        self.assertTrue('This username already exists' in response.body)
 
     def test_register_err_same_email(self):
         response = self.app.post(url(controller='login', action='register'),
@@ -88,7 +92,7 @@
                                              'name':'test',
                                              'lastname':'test'})
 
-        assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
+        self.assertEqual(response.status , '200 OK')
         assert 'This e-mail address is already taken' in response.body
 
     def test_register_err_same_email_case_sensitive(self):
@@ -99,7 +103,7 @@
                                              'email':'TesT_Admin@mail.COM',
                                              'name':'test',
                                              'lastname':'test'})
-        assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
+        self.assertEqual(response.status , '200 OK')
         assert 'This e-mail address is already taken' in response.body
 
     def test_register_err_wrong_data(self):
@@ -110,7 +114,7 @@
                                              'email':'goodmailm',
                                              'name':'test',
                                              'lastname':'test'})
-        assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
+        self.assertEqual(response.status , '200 OK')
         assert 'An email address must contain a single @' in response.body
         assert 'Enter a value 6 characters long or more' in response.body
 
@@ -124,8 +128,7 @@
                                              'name':'test',
                                              'lastname':'test'})
 
-        print response.body
-        assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
+        self.assertEqual(response.status , '200 OK')
         assert 'An email address must contain a single @' in response.body
         assert ('Username may only contain '
                 'alphanumeric characters underscores, '
@@ -141,7 +144,7 @@
                                              'name':'test',
                                              'lastname':'test'})
 
-        assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
+        self.assertEqual(response.status , '200 OK')
         assert 'An email address must contain a single @' in response.body
         assert 'This username already exists' in response.body
 
@@ -156,8 +159,7 @@
                                              'name':'test',
                                              'lastname':'test'})
 
-        print response.body
-        assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
+        self.assertEqual(response.status , '200 OK')
         assert 'Invalid characters in password' in response.body
 
 
@@ -170,8 +172,7 @@
                                              'name':'test',
                                              'lastname':'test'})
 
-        assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
-        print response.body
+        self.assertEqual(response.status , '200 OK')
         assert 'Password do not match' in response.body
 
     def test_register_ok(self):
@@ -188,7 +189,7 @@
                                              'email':email,
                                              'name':name,
                                              'lastname':lastname})
-        assert response.status == '302 Found', 'Wrong response from register page got %s' % response.status
+        self.assertEqual(response.status , '302 Found')
         assert 'You have successfully registered into rhodecode' in response.session['flash'][0], 'No flash message about user registration'
 
         ret = self.sa.query(User).filter(User.username == 'test_regular4').one()
@@ -206,8 +207,9 @@
         assert "This e-mail address doesn't exist" in response.body, 'Missing error message about wrong email'
 
     def test_forgot_password(self):
-        response = self.app.get(url(controller='login', action='password_reset'))
-        assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
+        response = self.app.get(url(controller='login',
+                                    action='password_reset'))
+        self.assertEqual(response.status , '200 OK')
 
         username = 'test_password_reset_1'
         password = 'qweqwe'
@@ -215,16 +217,45 @@
         name = 'passwd'
         lastname = 'reset'
 
-        response = self.app.post(url(controller='login', action='register'),
-                                            {'username':username,
-                                             'password':password,
-                                             'password_confirmation':password,
-                                             'email':email,
-                                             'name':name,
-                                             'lastname':lastname})
-        #register new user for email test
-        response = self.app.post(url(controller='login', action='password_reset'),
-                                            {'email':email, })
-        print response.session['flash']
-        assert 'You have successfully registered into rhodecode' in response.session['flash'][0], 'No flash message about user registration'
-        assert 'Your new password was sent' in response.session['flash'][1], 'No flash message about password reset'
+        new = User()
+        new.username = username
+        new.password = password
+        new.email = email
+        new.name = name
+        new.lastname = lastname
+        new.api_key = generate_api_key(username)
+        self.sa.add(new)
+        self.sa.commit()
+
+        response = self.app.post(url(controller='login',
+                                     action='password_reset'),
+                                 {'email':email, })
+
+        self.checkSessionFlash(response, 'Your password reset link was sent')
+
+        response = response.follow()
+
+        # BAD KEY
+
+        key = "bad"
+        response = self.app.get(url(controller='login',
+                                    action='password_reset_confirmation',
+                                    key=key))
+        self.assertEqual(response.status, '302 Found')
+        self.assertTrue(response.location.endswith(url('reset_password')))
+
+        # GOOD KEY
+
+        key = User.by_username(username).api_key
+
+        response = self.app.get(url(controller='login',
+                                    action='password_reset_confirmation',
+                                    key=key))
+        self.assertEqual(response.status, '302 Found')
+        self.assertTrue(response.location.endswith(url('login_home')))
+
+        self.checkSessionFlash(response,
+                               ('Your password reset was successful, '
+                                'new password has been sent to your email'))
+
+        response = response.follow()
--- a/setup.cfg	Sat Jul 02 20:38:54 2011 +0200
+++ b/setup.cfg	Mon Jul 04 18:07:49 2011 +0200
@@ -6,10 +6,10 @@
 find_links = http://www.pylonshq.com/download/
 
 [nosetests]
-verbose=False
+verbose=True
 verbosity=2
 with-pylons=test.ini
-detailed-errors=0
+detailed-errors=1
 nologcapture=1
 
 # Babel configuration