Changeset - febb3f954509
[Not reviewed]
rhodecode/config/routing.py
Show inline comments
 
@@ -291,12 +291,16 @@ def make_map(config):
 
    rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
 
                 action='register')
 

	
 
    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',
 
                conditions=dict(function=check_repo))
 

	
 
    rmap.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
rhodecode/controllers/login.py
Show inline comments
 
@@ -126,14 +126,14 @@ class LoginController(BaseController):
 
        user_model = UserModel()
 
        if request.POST:
 

	
 
            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'))
 

	
 
            except formencode.Invalid, errors:
 
                return htmlfill.render(
 
                    render('/password_reset.html'),
 
@@ -141,11 +141,28 @@ class LoginController(BaseController):
 
                    errors=errors.error_dict or {},
 
                    prefix_error=False,
 
                    encoding="UTF-8")
 

	
 
        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()
 
        log.info('Logging out and setting user as Empty')
 
        redirect(url('home'))
rhodecode/lib/celerylib/tasks.py
Show inline comments
 
@@ -31,13 +31,13 @@ import logging
 
from os.path import dirname as dn, join as jn
 

	
 
from time import mktime
 
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
 
from rhodecode.lib.celerylib import run_task, locked_task, str2bool, \
 
    __get_lockkey, LockHeld, DaemonLock
 
from rhodecode.lib.helpers import person
 
@@ -246,12 +246,51 @@ def get_commits_stats(repo_name, ts_min_
 
            run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
 
        return True
 
    except LockHeld:
 
        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):
 
    try:
 
        log = reset_user_password.get_logger()
 
    except:
 
@@ -277,14 +316,14 @@ def reset_user_password(user_email):
 

	
 
        except:
 
            log.error(traceback.format_exc())
 
            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:
 
        log.error('Failed to update user password')
 
        log.error(traceback.format_exc())
 

	
rhodecode/lib/smtp_mailer.py
Show inline comments
 
@@ -71,19 +71,25 @@ class SmtpMailer(object):
 
        #but only if we have them
 
        if self.user and self.passwd:
 
            smtp_serv.login(self.user, self.passwd)
 

	
 
        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)
 

	
 
        smtp_serv.sendmail(self.mail_from, recipients, msg.as_string())
 
        logging.info('MAIL SEND TO: %s' % recipients)
rhodecode/model/db.py
Show inline comments
 
@@ -240,12 +240,17 @@ class User(Base, BaseModel):
 
    def by_username(cls, username, case_insensitive=False):
 
        if case_insensitive:
 
            return Session.query(cls).filter(cls.username.like(username)).one()
 
        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"""
 

	
 
        self.last_login = datetime.datetime.now()
 
        Session.add(self)
 
        Session.commit()
rhodecode/model/user.py
Show inline comments
 
@@ -210,12 +210,16 @@ class UserModel(BaseModel):
 
            self.sa.commit()
 
        except:
 
            log.error(traceback.format_exc())
 
            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'])
 

	
 
    def fill_data(self, auth_user, user_id=None, api_key=None):
 
        """
rhodecode/public/css/style.css
Show inline comments
 
@@ -195,13 +195,14 @@ padding:0 10px;
 
margin-bottom:5px !important;
 
-webkit-border-radius: 0px 0px 8px 8px;
 
-khtml-border-radius: 0px 0px 8px 8px; 
 
-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 {
 
list-style:none;
 
float:left;
 
margin:8px 0 0;
 
@@ -1380,12 +1381,19 @@ background-color: rgb(0, 51, 103);
 
z-index: 999; 
 
height: 150px;
 
position: absolute;
 
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{
 
padding-top: 2px;
 
padding-left:10px;
 
}
rhodecode/templates/base/base.html
Show inline comments
 
@@ -27,13 +27,13 @@
 
			            <div class="input">
 
			                ${h.password('password',class_='focus',size=40)}
 
			            </div>
 
			            
 
			        </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>
 
			${h.end_form()}
 
			<script type="text/javascript">
 
			YUE.on('quick_login_link','click',function(e){
rhodecode/templates/password_reset.html
Show inline comments
 
@@ -25,13 +25,13 @@
 
	                </div>
 
	             </div>
 
	                        
 
	            <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>
 
	    </div>
 
	    ${h.end_form()}
 
        <script type="text/javascript">
rhodecode/templates/password_reset_confirmation.html
Show inline comments
 
new file 100644
rhodecode/tests/functional/test_login.py
Show inline comments
 
# -*- 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
 

	
 

	
 
class TestLoginController(TestController):
 

	
 
    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):
 
        response = self.app.post(url(controller='login', action='index'),
 
                                 {'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'),
 
                                            {'username':'test_admin',
 
                                             'password':'test12',
 
                                             'password_confirmation':'test12',
 
                                             'email':'goodmail@domain.com',
 
                                             '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'),
 
                                            {'username':'test_admin_0',
 
                                             'password':'test12',
 
                                             'password_confirmation':'test12',
 
                                             '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_same_email_case_sensitive(self):
 
        response = self.app.post(url(controller='login', action='register'),
 
                                            {'username':'test_admin_1',
 
                                             'password':'test12',
 
                                             'password_confirmation':'test12',
 
                                             '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):
 
        response = self.app.post(url(controller='login', action='register'),
 
                                            {'username':'xs',
 
                                             'password':'test',
 
                                             'password_confirmation':'test',
 
                                             '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
 

	
 

	
 
    def test_register_err_username(self):
 
        response = self.app.post(url(controller='login', action='register'),
 
@@ -121,14 +125,13 @@ class TestLoginController(TestController
 
                                             'password':'test12',
 
                                             'password_confirmation':'test12',
 
                                             'email':'goodmailm',
 
                                             '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, '
 
                'periods or dashes and must begin with '
 
                'alphanumeric character') in response.body
 

	
 
@@ -138,13 +141,13 @@ class TestLoginController(TestController
 
                                             'password':'test12',
 
                                             'password_confirmation':'test12',
 
                                             '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 'This username already exists' in response.body
 

	
 

	
 

	
 
    def test_register_special_chars(self):
 
@@ -153,28 +156,26 @@ class TestLoginController(TestController
 
                                             'password':'ąćźżąśśśś',
 
                                             'password_confirmation':'ąćźżąśśśś',
 
                                             'email':'goodmailm@test.plx',
 
                                             '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
 

	
 

	
 
    def test_register_password_mismatch(self):
 
        response = self.app.post(url(controller='login', action='register'),
 
                                            {'username':'xs',
 
                                             'password':'123qwe',
 
                                             'password_confirmation':'qwe123',
 
                                             'email':'goodmailm@test.plxa',
 
                                             '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):
 
        username = 'test_regular4'
 
        password = 'qweqwe'
 
        email = 'marcin@test.com'
 
@@ -185,13 +186,13 @@ class TestLoginController(TestController
 
                                            {'username':username,
 
                                             'password':password,
 
                                             'password_confirmation':password,
 
                                             '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()
 
        assert ret.username == username , 'field mismatch %s %s' % (ret.username, username)
 
        assert check_password(password, ret.password) == True , 'password mismatch'
 
        assert ret.email == email , 'field mismatch %s %s' % (ret.email, email)
 
@@ -203,28 +204,58 @@ class TestLoginController(TestController
 
        response = self.app.post(url(controller='login', action='password_reset'),
 
                                            {'email':'marcin@wrongmail.org', })
 

	
 
        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'
 
        email = 'marcin@python-works.com'
 
        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()
setup.cfg
Show inline comments
 
@@ -3,16 +3,16 @@ tag_build = beta
 
tag_svn_revision = true
 

	
 
[easy_install]
 
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
 
[compile_catalog]
 
domain = rhodecode
 
directory = rhodecode/i18n
0 comments (0 inline, 0 general)