prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Migrate EMail, TAN and VASCO token to Python 3
  This slightly changes behavior: The VASCO token initialization now throws a ``ParameterError`` if the secret couldn't be decoded.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `privacyidea/lib/authcache.py`
  Content:
  ```
  1 # -*- coding: utf-8 -*-
  2 #
  3 #  2017-08-11 Cornelius Kölbel <cornelius.koelbel@netknights.it>
  4 #             initial writeup
  5 #
  6 #  License:  AGPLv3
  7 #  contact:  http://www.privacyidea.org
  8 #
  9 # This code is free software; you can redistribute it and/or
  10 # modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  11 # License as published by the Free Software Foundation; either
  12 # version 3 of the License, or any later version.
  13 #
  14 # This code is distributed in the hope that it will be useful,
  15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 # MERCHANTABILITY or FITNE7SS FOR A PARTICULAR PURPOSE.  See the
  17 # GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  18 #
  19 # You should have received a copy of the GNU Affero General Public
  20 # License along with this program.  If not, see <http://www.gnu.org/licenses/>.
  21 #
  22 import binascii
  23 
  24 from ..models import AuthCache, db
  25 from sqlalchemy import and_
  26 from privacyidea.lib.crypto import hash
  27 import datetime
  28 import logging
  29 
  30 log = logging.getLogger(__name__)
  31 
  32 
  33 def _hash_password(password):
  34     return binascii.hexlify(hash(password, seed=""))
  35 
  36 
  37 def add_to_cache(username, realm, resolver, password):
  38     # Can not store timezone aware timestamps!
  39     first_auth = datetime.datetime.utcnow()
  40     auth_hash = _hash_password(password)
  41     record = AuthCache(username, realm, resolver, auth_hash, first_auth, first_auth)
  42     log.debug('Adding record to auth cache: ({!r}, {!r}, {!r}, {!r})'.format(
  43         username, realm, resolver, auth_hash))
  44     r = record.save()
  45     return r
  46 
  47 
  48 def update_cache_last_auth(cache_id):
  49     last_auth = datetime.datetime.utcnow()
  50     AuthCache.query.filter(
  51         AuthCache.id == cache_id).update({"last_auth": last_auth})
  52     db.session.commit()
  53 
  54 
  55 def delete_from_cache(username, realm, resolver, password):
  56     r = db.session.query(AuthCache).filter(AuthCache.username == username,
  57                                        AuthCache.realm == realm,
  58                                        AuthCache.resolver == resolver,
  59                                        AuthCache.authentication ==
  60                                        _hash_password(password)).delete()
  61     db.session.commit()
  62     return r
  63 
  64 
  65 def verify_in_cache(username, realm, resolver, password,
  66                     first_auth = None,
  67                     last_auth = None):
  68     """
  69     Verify if the given credentials are cached and if the time is correct.
  70     
  71     :param username: 
  72     :param realm: 
  73     :param resolver: 
  74     :param password: 
  75     :param first_auth: The timestamp when the entry was first written to the 
  76         cache. Only find newer entries 
  77     :param last_auth: The timestamp when the entry was last successfully 
  78         verified. Only find newer entries 
  79     :return: 
  80     """
  81     conditions = []
  82     conditions.append(AuthCache.username == username)
  83     conditions.append(AuthCache.realm == realm)
  84     conditions.append(AuthCache.resolver == resolver)
  85     auth_hash = _hash_password(password)
  86     conditions.append(AuthCache.authentication == auth_hash)
  87 
  88     if first_auth:
  89         conditions.append(AuthCache.first_auth > first_auth)
  90     if last_auth:
  91         conditions.append(AuthCache.last_auth > last_auth)
  92 
  93     filter_condition = and_(*conditions)
  94     r = AuthCache.query.filter(filter_condition).first()
  95     result = bool(r)
  96 
  97     if result:
  98         # Update the last_auth
  99         update_cache_last_auth(r.id)
  100 
  101     else:
  102         # Delete older entries
  103         delete_from_cache(username, realm, resolver, password)
  104 
  105     return result
  106 
  107
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/privacyidea/lib/authcache.py b/privacyidea/lib/authcache.py
  --- a/privacyidea/lib/authcache.py
  +++ b/privacyidea/lib/authcache.py
  @@ -62,6 +62,21 @@
       return r
   
   
  +def cleanup(minutes):
  +    """
  +    Will delete all authcache entries, where last_auth column is older than
  +    the given minutes.
  +
  +    :param minutes: Age of the last_authentication in minutes
  +    :type minutes: int
  +    :return:
  +    """
  +    cleanuptime = datetime.datetime.utcnow() - datetime.timedelta(minutes=minutes)
  +    r = db.session.query(AuthCache).filter(AuthCache.last_auth < cleanuptime).delete()
  +    db.session.commit()
  +    return r
  +
  +
   def verify_in_cache(username, realm, resolver, password,
                       first_auth = None,
                       last_auth = None):
  
verification_info: {"golden_diff": "diff --git a/privacyidea/lib/authcache.py b/privacyidea/lib/authcache.py\n--- a/privacyidea/lib/authcache.py\n+++ b/privacyidea/lib/authcache.py\n@@ -62,6 +62,21 @@\n     return r\n \n \n+def cleanup(minutes):\n+    \"\"\"\n+    Will delete all authcache entries, where last_auth column is older than\n+    the given minutes.\n+\n+    :param minutes: Age of the last_authentication in minutes\n+    :type minutes: int\n+    :return:\n+    \"\"\"\n+    cleanuptime = datetime.datetime.utcnow() - datetime.timedelta(minutes=minutes)\n+    r = db.session.query(AuthCache).filter(AuthCache.last_auth < cleanuptime).delete()\n+    db.session.commit()\n+    return r\n+\n+\n def verify_in_cache(username, realm, resolver, password,\n                     first_auth = None,\n                     last_auth = None):\n", "issue": "Migrate EMail, TAN and VASCO token to Python 3\nThis slightly changes behavior: The VASCO token initialization now throws a ``ParameterError`` if the secret couldn't be decoded.\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n#\n#  2017-08-11 Cornelius K\u00f6lbel <cornelius.koelbel@netknights.it>\n#             initial writeup\n#\n#  License:  AGPLv3\n#  contact:  http://www.privacyidea.org\n#\n# This code is free software; you can redistribute it and/or\n# modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE\n# License as published by the Free Software Foundation; either\n# version 3 of the License, or any later version.\n#\n# This code is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNE7SS FOR A PARTICULAR PURPOSE.  See the\n# GNU AFFERO GENERAL PUBLIC LICENSE for more details.\n#\n# You should have received a copy of the GNU Affero General Public\n# License along with this program.  If not, see <http://www.gnu.org/licenses/>.\n#\nimport binascii\n\nfrom ..models import AuthCache, db\nfrom sqlalchemy import and_\nfrom privacyidea.lib.crypto import hash\nimport datetime\nimport logging\n\nlog = logging.getLogger(__name__)\n\n\ndef _hash_password(password):\n    return binascii.hexlify(hash(password, seed=\"\"))\n\n\ndef add_to_cache(username, realm, resolver, password):\n    # Can not store timezone aware timestamps!\n    first_auth = datetime.datetime.utcnow()\n    auth_hash = _hash_password(password)\n    record = AuthCache(username, realm, resolver, auth_hash, first_auth, first_auth)\n    log.debug('Adding record to auth cache: ({!r}, {!r}, {!r}, {!r})'.format(\n        username, realm, resolver, auth_hash))\n    r = record.save()\n    return r\n\n\ndef update_cache_last_auth(cache_id):\n    last_auth = datetime.datetime.utcnow()\n    AuthCache.query.filter(\n        AuthCache.id == cache_id).update({\"last_auth\": last_auth})\n    db.session.commit()\n\n\ndef delete_from_cache(username, realm, resolver, password):\n    r = db.session.query(AuthCache).filter(AuthCache.username == username,\n                                       AuthCache.realm == realm,\n                                       AuthCache.resolver == resolver,\n                                       AuthCache.authentication ==\n                                       _hash_password(password)).delete()\n    db.session.commit()\n    return r\n\n\ndef verify_in_cache(username, realm, resolver, password,\n                    first_auth = None,\n                    last_auth = None):\n    \"\"\"\n    Verify if the given credentials are cached and if the time is correct.\n    \n    :param username: \n    :param realm: \n    :param resolver: \n    :param password: \n    :param first_auth: The timestamp when the entry was first written to the \n        cache. Only find newer entries \n    :param last_auth: The timestamp when the entry was last successfully \n        verified. Only find newer entries \n    :return: \n    \"\"\"\n    conditions = []\n    conditions.append(AuthCache.username == username)\n    conditions.append(AuthCache.realm == realm)\n    conditions.append(AuthCache.resolver == resolver)\n    auth_hash = _hash_password(password)\n    conditions.append(AuthCache.authentication == auth_hash)\n\n    if first_auth:\n        conditions.append(AuthCache.first_auth > first_auth)\n    if last_auth:\n        conditions.append(AuthCache.last_auth > last_auth)\n\n    filter_condition = and_(*conditions)\n    r = AuthCache.query.filter(filter_condition).first()\n    result = bool(r)\n\n    if result:\n        # Update the last_auth\n        update_cache_last_auth(r.id)\n\n    else:\n        # Delete older entries\n        delete_from_cache(username, realm, resolver, password)\n\n    return result\n\n", "path": "privacyidea/lib/authcache.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n#\n#  2017-08-11 Cornelius K\u00f6lbel <cornelius.koelbel@netknights.it>\n#             initial writeup\n#\n#  License:  AGPLv3\n#  contact:  http://www.privacyidea.org\n#\n# This code is free software; you can redistribute it and/or\n# modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE\n# License as published by the Free Software Foundation; either\n# version 3 of the License, or any later version.\n#\n# This code is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNE7SS FOR A PARTICULAR PURPOSE.  See the\n# GNU AFFERO GENERAL PUBLIC LICENSE for more details.\n#\n# You should have received a copy of the GNU Affero General Public\n# License along with this program.  If not, see <http://www.gnu.org/licenses/>.\n#\nimport binascii\n\nfrom ..models import AuthCache, db\nfrom sqlalchemy import and_\nfrom privacyidea.lib.crypto import hash\nimport datetime\nimport logging\n\nlog = logging.getLogger(__name__)\n\n\ndef _hash_password(password):\n    return binascii.hexlify(hash(password, seed=\"\"))\n\n\ndef add_to_cache(username, realm, resolver, password):\n    # Can not store timezone aware timestamps!\n    first_auth = datetime.datetime.utcnow()\n    auth_hash = _hash_password(password)\n    record = AuthCache(username, realm, resolver, auth_hash, first_auth, first_auth)\n    log.debug('Adding record to auth cache: ({!r}, {!r}, {!r}, {!r})'.format(\n        username, realm, resolver, auth_hash))\n    r = record.save()\n    return r\n\n\ndef update_cache_last_auth(cache_id):\n    last_auth = datetime.datetime.utcnow()\n    AuthCache.query.filter(\n        AuthCache.id == cache_id).update({\"last_auth\": last_auth})\n    db.session.commit()\n\n\ndef delete_from_cache(username, realm, resolver, password):\n    r = db.session.query(AuthCache).filter(AuthCache.username == username,\n                                       AuthCache.realm == realm,\n                                       AuthCache.resolver == resolver,\n                                       AuthCache.authentication ==\n                                       _hash_password(password)).delete()\n    db.session.commit()\n    return r\n\n\ndef cleanup(minutes):\n    \"\"\"\n    Will delete all authcache entries, where last_auth column is older than\n    the given minutes.\n\n    :param minutes: Age of the last_authentication in minutes\n    :type minutes: int\n    :return:\n    \"\"\"\n    cleanuptime = datetime.datetime.utcnow() - datetime.timedelta(minutes=minutes)\n    r = db.session.query(AuthCache).filter(AuthCache.last_auth < cleanuptime).delete()\n    db.session.commit()\n    return r\n\n\ndef verify_in_cache(username, realm, resolver, password,\n                    first_auth = None,\n                    last_auth = None):\n    \"\"\"\n    Verify if the given credentials are cached and if the time is correct.\n    \n    :param username: \n    :param realm: \n    :param resolver: \n    :param password: \n    :param first_auth: The timestamp when the entry was first written to the \n        cache. Only find newer entries \n    :param last_auth: The timestamp when the entry was last successfully \n        verified. Only find newer entries \n    :return: \n    \"\"\"\n    conditions = []\n    conditions.append(AuthCache.username == username)\n    conditions.append(AuthCache.realm == realm)\n    conditions.append(AuthCache.resolver == resolver)\n    auth_hash = _hash_password(password)\n    conditions.append(AuthCache.authentication == auth_hash)\n\n    if first_auth:\n        conditions.append(AuthCache.first_auth > first_auth)\n    if last_auth:\n        conditions.append(AuthCache.last_auth > last_auth)\n\n    filter_condition = and_(*conditions)\n    r = AuthCache.query.filter(filter_condition).first()\n    result = bool(r)\n\n    if result:\n        # Update the last_auth\n        update_cache_last_auth(r.id)\n\n    else:\n        # Delete older entries\n        delete_from_cache(username, realm, resolver, password)\n\n    return result\n\n", "path": "privacyidea/lib/authcache.py"}]}