# HG changeset patch # User Sylvain Thénault # Date 1295437628 -3600 # Node ID 5a0c2cfc19bf3f649a5acd0e920ab58b298d67a7 # Parent f87cd875c6dbbced1a39b03032175870c6193d53 [repository auth] cleanup email login by turning it into a proper repo-side authentication plugin diff -r f87cd875c6db -r 5a0c2cfc19bf server/repository.py --- a/server/repository.py Wed Jan 19 12:47:06 2011 +0100 +++ b/server/repository.py Wed Jan 19 12:47:08 2011 +0100 @@ -430,33 +430,24 @@ except ZeroDivisionError: pass - def _login_from_email(self, login): - session = self.internal_session() - try: - rset = session.execute('Any L WHERE U login L, U primary_email M, ' - 'M address %(login)s', {'login': login}, - build_descr=False) - if rset.rowcount == 1: - login = rset[0][0] - finally: - session.close() - return login - - def authenticate_user(self, session, login, **kwargs): - """validate login / password, raise AuthenticationError on failure - return associated CWUser instance on success + def check_auth_info(self, session, login, authinfo): + """validate authentication, raise AuthenticationError on failure, return + associated CWUser's eid on success. """ - if self.vreg.config['allow-email-login'] and '@' in login: - login = self._login_from_email(login) for source in self.sources: if source.support_entity('CWUser'): try: - eid = source.authenticate(session, login, **kwargs) - break + return source.authenticate(session, login, **authinfo) except AuthenticationError: continue else: raise AuthenticationError('authentication failed with all sources') + + def authenticate_user(self, session, login, **authinfo): + """validate login / password, raise AuthenticationError on failure + return associated CWUser instance on success + """ + eid = self.check_auth_info(session, login, authinfo) cwuser = self._build_user(session, eid) if self.config.consider_user_state and \ not cwuser.cw_adapt_to('IWorkflowable').state in cwuser.AUTHENTICABLE_STATES: diff -r f87cd875c6db -r 5a0c2cfc19bf server/sources/native.py --- a/server/sources/native.py Wed Jan 19 12:47:06 2011 +0100 +++ b/server/sources/native.py Wed Jan 19 12:47:08 2011 +0100 @@ -266,6 +266,8 @@ def __init__(self, repo, source_config, *args, **kwargs): SQLAdapterMixIn.__init__(self, source_config) self.authentifiers = [LoginPasswordAuthentifier(self)] + if repo.config['allow-email-login']: + self.authentifiers.insert(0, EmailPasswordAuthentifier(self)) AbstractSource.__init__(self, repo, source_config, *args, **kwargs) # sql generator self._rql_sqlgen = self.sqlgen_class(self.schema, self.dbhelper, @@ -1495,3 +1497,19 @@ return rset[0][0] except IndexError: raise AuthenticationError('bad password') + + +class EmailPasswordAuthentifier(BaseAuthentifier): + def authenticate(self, session, login, **authinfo): + # email_auth flag prevent from infinite recursion (call to + # repo.check_auth_info at the end of this method may lead us here again) + if not '@' in login or authinfo.pop('email_auth', None): + raise AuthenticationError('not an email') + rset = session.execute('Any L WHERE U login L, U primary_email M, ' + 'M address %(login)s', {'login': login}, + build_descr=False) + if rset.rowcount != 1: + raise AuthenticationError('unexisting email') + login = rset.rows[0][0] + authinfo['email_auth'] = True + return self.source.repo.check_auth_info(session, login, authinfo) diff -r f87cd875c6db -r 5a0c2cfc19bf web/test/unittest_application.py --- a/web/test/unittest_application.py Wed Jan 19 12:47:06 2011 +0100 +++ b/web/test/unittest_application.py Wed Jan 19 12:47:08 2011 +0100 @@ -22,7 +22,7 @@ from urllib import unquote from logilab.common.testlib import TestCase, unittest_main -from logilab.common.decorators import clear_cache +from logilab.common.decorators import clear_cache, classproperty from cubicweb import AuthenticationError, Unauthorized from cubicweb.devtools.testlib import CubicWebTC @@ -157,6 +157,15 @@ raise self.app.error_handler = raise_hdlr + @classproperty + def config(cls): + try: + return cls.__dict__['_config'] + except KeyError: + config = super(ApplicationTC, cls).config + config.global_set_option('allow-email-login', True) + return config + def test_cnx_user_groups_sync(self): user = self.user() self.assertEqual(user.groups, set(('managers',))) @@ -347,7 +356,7 @@ self.execute('INSERT EmailAddress X: X address %(address)s, U primary_email X ' 'WHERE U login %(login)s', {'address': address, 'login': login}) self.commit() - # option allow-email-login not set + # # option allow-email-login not set req, origsession = self.init_authentication('cookie') # req.form['__login'] = address # req.form['__password'] = self.admpassword