437 return not rtype in NONSYSTEM_RELATIONS |
437 return not rtype in NONSYSTEM_RELATIONS |
438 # due to current multi-sources implementation, the system source |
438 # due to current multi-sources implementation, the system source |
439 # can't claim not supporting a relation |
439 # can't claim not supporting a relation |
440 return True #not rtype == 'content_for' |
440 return True #not rtype == 'content_for' |
441 |
441 |
442 def authenticate(self, session, login, **kwargs): |
442 def authenticate(self, cnx, login, **kwargs): |
443 """return CWUser eid for the given login and other authentication |
443 """return CWUser eid for the given login and other authentication |
444 information found in kwargs, else raise `AuthenticationError` |
444 information found in kwargs, else raise `AuthenticationError` |
445 """ |
445 """ |
446 for authentifier in self.authentifiers: |
446 for authentifier in self.authentifiers: |
447 try: |
447 try: |
448 return authentifier.authenticate(session, login, **kwargs) |
448 return authentifier.authenticate(cnx, login, **kwargs) |
449 except AuthenticationError: |
449 except AuthenticationError: |
450 continue |
450 continue |
451 raise AuthenticationError() |
451 raise AuthenticationError() |
452 |
452 |
453 def syntax_tree_search(self, session, union, args=None, cachekey=None, |
453 def syntax_tree_search(self, session, union, args=None, cachekey=None, |
1448 if 'CWUser' in schema: # probably an empty schema if not true... |
1448 if 'CWUser' in schema: # probably an empty schema if not true... |
1449 # rql syntax trees used to authenticate users |
1449 # rql syntax trees used to authenticate users |
1450 self._passwd_rqlst = self.source.compile_rql(self.passwd_rql, self._sols) |
1450 self._passwd_rqlst = self.source.compile_rql(self.passwd_rql, self._sols) |
1451 self._auth_rqlst = self.source.compile_rql(self.auth_rql, self._sols) |
1451 self._auth_rqlst = self.source.compile_rql(self.auth_rql, self._sols) |
1452 |
1452 |
1453 def authenticate(self, session, login, password=None, **kwargs): |
1453 def authenticate(self, cnx, login, password=None, **kwargs): |
1454 """return CWUser eid for the given login/password if this account is |
1454 """return CWUser eid for the given login/password if this account is |
1455 defined in this source, else raise `AuthenticationError` |
1455 defined in this source, else raise `AuthenticationError` |
1456 |
1456 |
1457 two queries are needed since passwords are stored crypted, so we have |
1457 two queries are needed since passwords are stored crypted, so we have |
1458 to fetch the salt first |
1458 to fetch the salt first |
1459 """ |
1459 """ |
1460 args = {'login': login, 'pwd' : None} |
1460 args = {'login': login, 'pwd' : None} |
1461 if password is not None: |
1461 if password is not None: |
1462 rset = self.source.syntax_tree_search(session, self._passwd_rqlst, args) |
1462 rset = self.source.syntax_tree_search(cnx, self._passwd_rqlst, args) |
1463 try: |
1463 try: |
1464 pwd = rset[0][0] |
1464 pwd = rset[0][0] |
1465 except IndexError: |
1465 except IndexError: |
1466 raise AuthenticationError('bad login') |
1466 raise AuthenticationError('bad login') |
1467 if pwd is None: |
1467 if pwd is None: |
1468 # if pwd is None but a password is provided, something is wrong |
1468 # if pwd is None but a password is provided, something is wrong |
1469 raise AuthenticationError('bad password') |
1469 raise AuthenticationError('bad password') |
1470 # passwords are stored using the Bytes type, so we get a StringIO |
1470 # passwords are stored using the Bytes type, so we get a StringIO |
1471 args['pwd'] = Binary(crypt_password(password, pwd.getvalue())) |
1471 args['pwd'] = Binary(crypt_password(password, pwd.getvalue())) |
1472 # get eid from login and (crypted) password |
1472 # get eid from login and (crypted) password |
1473 rset = self.source.syntax_tree_search(session, self._auth_rqlst, args) |
1473 rset = self.source.syntax_tree_search(cnx, self._auth_rqlst, args) |
1474 try: |
1474 try: |
1475 user = rset[0][0] |
1475 user = rset[0][0] |
1476 # If the stored hash uses a deprecated scheme (e.g. DES or MD5 used |
1476 # If the stored hash uses a deprecated scheme (e.g. DES or MD5 used |
1477 # before 3.14.7), update with a fresh one |
1477 # before 3.14.7), update with a fresh one |
1478 if pwd.getvalue(): |
1478 if pwd.getvalue(): |
1479 verify, newhash = verify_and_update(password, pwd.getvalue()) |
1479 verify, newhash = verify_and_update(password, pwd.getvalue()) |
1480 if not verify: # should not happen, but... |
1480 if not verify: # should not happen, but... |
1481 raise AuthenticationError('bad password') |
1481 raise AuthenticationError('bad password') |
1482 if newhash: |
1482 if newhash: |
1483 session.system_sql("UPDATE %s SET %s=%%(newhash)s WHERE %s=%%(login)s" % ( |
1483 cnx.system_sql("UPDATE %s SET %s=%%(newhash)s WHERE %s=%%(login)s" % ( |
1484 SQL_PREFIX + 'CWUser', |
1484 SQL_PREFIX + 'CWUser', |
1485 SQL_PREFIX + 'upassword', |
1485 SQL_PREFIX + 'upassword', |
1486 SQL_PREFIX + 'login'), |
1486 SQL_PREFIX + 'login'), |
1487 {'newhash': self.source._binary(newhash), |
1487 {'newhash': self.source._binary(newhash), |
1488 'login': login}) |
1488 'login': login}) |
1489 session.commit(free_cnxset=False) |
1489 cnx.commit(free_cnxset=False) |
1490 return user |
1490 return user |
1491 except IndexError: |
1491 except IndexError: |
1492 raise AuthenticationError('bad password') |
1492 raise AuthenticationError('bad password') |
1493 |
1493 |
1494 |
1494 |
1495 class EmailPasswordAuthentifier(BaseAuthentifier): |
1495 class EmailPasswordAuthentifier(BaseAuthentifier): |
1496 def authenticate(self, session, login, **authinfo): |
1496 def authenticate(self, cnx, login, **authinfo): |
1497 # email_auth flag prevent from infinite recursion (call to |
1497 # email_auth flag prevent from infinite recursion (call to |
1498 # repo.check_auth_info at the end of this method may lead us here again) |
1498 # repo.check_auth_info at the end of this method may lead us here again) |
1499 if not '@' in login or authinfo.pop('email_auth', None): |
1499 if not '@' in login or authinfo.pop('email_auth', None): |
1500 raise AuthenticationError('not an email') |
1500 raise AuthenticationError('not an email') |
1501 rset = session.execute('Any L WHERE U login L, U primary_email M, ' |
1501 rset = cnx.execute('Any L WHERE U login L, U primary_email M, ' |
1502 'M address %(login)s', {'login': login}, |
1502 'M address %(login)s', {'login': login}, |
1503 build_descr=False) |
1503 build_descr=False) |
1504 if rset.rowcount != 1: |
1504 if rset.rowcount != 1: |
1505 raise AuthenticationError('unexisting email') |
1505 raise AuthenticationError('unexisting email') |
1506 login = rset.rows[0][0] |
1506 login = rset.rows[0][0] |
1507 authinfo['email_auth'] = True |
1507 authinfo['email_auth'] = True |
1508 return self.source.repo.check_auth_info(session, login, authinfo) |
1508 return self.source.repo.check_auth_info(cnx, login, authinfo) |
1509 |
1509 |
1510 |
1510 |
1511 class DatabaseIndependentBackupRestore(object): |
1511 class DatabaseIndependentBackupRestore(object): |
1512 """Helper class to perform db backend agnostic backup and restore |
1512 """Helper class to perform db backend agnostic backup and restore |
1513 |
1513 |