# HG changeset patch # User Julien Cristau # Date 1390999422 -3600 # Node ID 88dc96fc9fc10abb40a8bf42bbf643ca8da1b962 # Parent 241b1232ed7f3d02d48d8bb5435b5cd92b926e2e [server] use a connection instead of a session for user authentication diff -r 241b1232ed7f -r 88dc96fc9fc1 server/repository.py --- a/server/repository.py Tue Feb 11 17:29:58 2014 +0100 +++ b/server/repository.py Wed Jan 29 13:43:42 2014 +0100 @@ -448,7 +448,7 @@ except ZeroDivisionError: pass - def check_auth_info(self, session, login, authinfo): + def check_auth_info(self, cnx, login, authinfo): """validate authentication, raise AuthenticationError on failure, return associated CWUser's eid on success. """ @@ -457,30 +457,31 @@ for source in self.sources_by_uri.itervalues(): if self.config.source_enabled(source) and source.support_entity('CWUser'): try: - return source.authenticate(session, login, **authinfo) + with cnx.ensure_cnx_set: + return source.authenticate(cnx, login, **authinfo) except AuthenticationError: continue else: raise AuthenticationError('authentication failed with all sources') - def authenticate_user(self, session, login, **authinfo): + def authenticate_user(self, cnx, 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) + eid = self.check_auth_info(cnx, login, authinfo) + cwuser = self._build_user(cnx, eid) if self.config.consider_user_state and \ not cwuser.cw_adapt_to('IWorkflowable').state in cwuser.AUTHENTICABLE_STATES: raise AuthenticationError('user is not in authenticable state') return cwuser - def _build_user(self, session, eid): + def _build_user(self, cnx, eid): """return a CWUser entity for user with the given eid""" - with session.ensure_cnx_set: + with cnx.ensure_cnx_set: cls = self.vreg['etypes'].etype_class('CWUser') - st = cls.fetch_rqlst(session.user, ordermethod=None) + st = cls.fetch_rqlst(cnx.user, ordermethod=None) st.add_eid_restriction(st.get_variable('X'), 'x', 'Substitute') - rset = session.execute(st.as_string(), {'x': eid}) + rset = cnx.execute(st.as_string(), {'x': eid}) assert len(rset) == 1, rset cwuser = rset.get_entity(0, 0) # pylint: disable=W0104 @@ -681,9 +682,9 @@ """ cnxprops = kwargs.pop('cnxprops', None) # use an internal connection - with self.internal_session() as session: + with self.internal_cnx() as cnx: # try to get a user object - user = self.authenticate_user(session, login, **kwargs) + user = self.authenticate_user(cnx, login, **kwargs) session = Session(user, self, cnxprops) if threading.currentThread() in self._pyro_sessions: # assume no pyro client does one get_repository followed by diff -r 241b1232ed7f -r 88dc96fc9fc1 server/sources/__init__.py --- a/server/sources/__init__.py Tue Feb 11 17:29:58 2014 +0100 +++ b/server/sources/__init__.py Wed Jan 29 13:43:42 2014 +0100 @@ -346,7 +346,7 @@ # user authentication api ################################################## - def authenticate(self, session, login, **kwargs): + def authenticate(self, cnx, login, **kwargs): """if the source support CWUser entity type, it should implement this method which should return CWUser eid for the given login/password if this account is defined in this source and valid login / password is @@ -356,7 +356,7 @@ # RQL query api ############################################################ - def syntax_tree_search(self, session, union, + def syntax_tree_search(self, cnx, union, args=None, cachekey=None, varmap=None, debug=0): """return result from this source for a rql query (actually from a rql syntax tree and a solution dictionary mapping each used variable to a diff -r 241b1232ed7f -r 88dc96fc9fc1 server/sources/ldapfeed.py --- a/server/sources/ldapfeed.py Tue Feb 11 17:29:58 2014 +0100 +++ b/server/sources/ldapfeed.py Wed Jan 29 13:43:42 2014 +0100 @@ -219,7 +219,7 @@ hostport = '%s:%s' % (hostport, PROTO_PORT[protocol]) return protocol, hostport - def authenticate(self, session, login, password=None, **kwargs): + def authenticate(self, cnx, login, password=None, **kwargs): """return CWUser eid for the given login/password if this account is defined in this source, else raise `AuthenticationError` @@ -237,7 +237,7 @@ searchstr = '(&%s)' % ''.join(searchfilter) # first search the user try: - user = self._search(session, self.user_base_dn, + user = self._search(cnx, self.user_base_dn, self.user_base_scope, searchstr)[0] except (IndexError, ldap.SERVER_DOWN): # no such user @@ -252,7 +252,7 @@ except Exception: self.error('while trying to authenticate %s', user, exc_info=True) raise AuthenticationError() - eid = self.repo.extid2eid(self, user['dn'], 'CWUser', session, {}) + eid = self.repo.extid2eid(self, user['dn'], 'CWUser', session=cnx, insert=False) if eid < 0: # user has been moved away from this source raise AuthenticationError() @@ -314,28 +314,28 @@ #from ldap import sasl #conn.sasl_interactive_bind_s('', sasl.gssapi()) - def _search(self, session, base, scope, + def _search(self, cnx, base, scope, searchstr='(objectClass=*)', attrs=()): """make an ldap query""" self.debug('ldap search %s %s %s %s %s', self.uri, base, scope, searchstr, list(attrs)) if self._conn is None: self._conn = self._connect() - cnx = self._conn + ldapcnx = self._conn try: - res = cnx.search_s(base, scope, searchstr, attrs) + res = ldapcnx.search_s(base, scope, searchstr, attrs) except ldap.PARTIAL_RESULTS: - res = cnx.result(all=0)[1] + res = ldapcnx.result(all=0)[1] except ldap.NO_SUCH_OBJECT: self.info('ldap NO SUCH OBJECT %s %s %s', base, scope, searchstr) - self._process_no_such_object(session, base) + self._process_no_such_object(cnx, base) return [] # except ldap.REFERRAL as e: - # cnx = self.handle_referral(e) + # ldapcnx = self.handle_referral(e) # try: - # res = cnx.search_s(base, scope, searchstr, attrs) + # res = ldapcnx.search_s(base, scope, searchstr, attrs) # except ldap.PARTIAL_RESULTS: - # res_type, res = cnx.result(all=0) + # res_type, res = ldapcnx.result(all=0) result = [] for rec_dn, rec_dict in res: # When used against Active Directory, "rec_dict" may not be @@ -380,7 +380,7 @@ itemdict[member] = [itemdict[member]] return itemdict - def _process_no_such_object(self, session, dn): + def _process_no_such_object(self, cnx, dn): """Some search return NO_SUCH_OBJECT error, handle this (usually because an object whose dn is no more existent in ldap as been encountered). diff -r 241b1232ed7f -r 88dc96fc9fc1 server/sources/native.py --- a/server/sources/native.py Tue Feb 11 17:29:58 2014 +0100 +++ b/server/sources/native.py Wed Jan 29 13:43:42 2014 +0100 @@ -439,13 +439,13 @@ # can't claim not supporting a relation return True #not rtype == 'content_for' - def authenticate(self, session, login, **kwargs): + def authenticate(self, cnx, login, **kwargs): """return CWUser eid for the given login and other authentication information found in kwargs, else raise `AuthenticationError` """ for authentifier in self.authentifiers: try: - return authentifier.authenticate(session, login, **kwargs) + return authentifier.authenticate(cnx, login, **kwargs) except AuthenticationError: continue raise AuthenticationError() @@ -1450,7 +1450,7 @@ self._passwd_rqlst = self.source.compile_rql(self.passwd_rql, self._sols) self._auth_rqlst = self.source.compile_rql(self.auth_rql, self._sols) - def authenticate(self, session, login, password=None, **kwargs): + def authenticate(self, cnx, login, password=None, **kwargs): """return CWUser eid for the given login/password if this account is defined in this source, else raise `AuthenticationError` @@ -1459,7 +1459,7 @@ """ args = {'login': login, 'pwd' : None} if password is not None: - rset = self.source.syntax_tree_search(session, self._passwd_rqlst, args) + rset = self.source.syntax_tree_search(cnx, self._passwd_rqlst, args) try: pwd = rset[0][0] except IndexError: @@ -1470,7 +1470,7 @@ # passwords are stored using the Bytes type, so we get a StringIO args['pwd'] = Binary(crypt_password(password, pwd.getvalue())) # get eid from login and (crypted) password - rset = self.source.syntax_tree_search(session, self._auth_rqlst, args) + rset = self.source.syntax_tree_search(cnx, self._auth_rqlst, args) try: user = rset[0][0] # If the stored hash uses a deprecated scheme (e.g. DES or MD5 used @@ -1480,32 +1480,32 @@ if not verify: # should not happen, but... raise AuthenticationError('bad password') if newhash: - session.system_sql("UPDATE %s SET %s=%%(newhash)s WHERE %s=%%(login)s" % ( + cnx.system_sql("UPDATE %s SET %s=%%(newhash)s WHERE %s=%%(login)s" % ( SQL_PREFIX + 'CWUser', SQL_PREFIX + 'upassword', SQL_PREFIX + 'login'), {'newhash': self.source._binary(newhash), 'login': login}) - session.commit(free_cnxset=False) + cnx.commit(free_cnxset=False) return user except IndexError: raise AuthenticationError('bad password') class EmailPasswordAuthentifier(BaseAuthentifier): - def authenticate(self, session, login, **authinfo): + def authenticate(self, cnx, 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, ' + rset = cnx.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) + return self.source.repo.check_auth_info(cnx, login, authinfo) class DatabaseIndependentBackupRestore(object):