We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Setting and then Clearing 'User Cache expiration in seconds' Breaks Authentication
# What did you try to do?

Clear/delete value for 'User Cache expiration in seconds' under System Config.

# What outcome did you expect?

Value reset to default of 0.

# What outcome did you experience?

Authentication breaks after value is cleared.  Error is logged to log file.

`
[2019-04-26 14:31:42,511][ERROR][privacyidea.lib.token:493] User information can not be retrieved: invalid literal for int() with base 10: ''
`

Changing UserCacheExpiration back to 0 directly in the config table in the pi database and restarting the Apache2 service resolves the issue.

# Configuration

* privacyIDEA Version: 3.0
* Installation method: ubuntu packages

## more details: 

* OS: Ubuntu 16.04LTS
* Webserver: Apache2
* Tokendatabase: MySQL


--- 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/usercache.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 #
3 #  2017-03-30   Friedrich Weber <friedrich.weber@netknights.it>
4 #               First ideas for a user cache to improve performance
5 #
6 # This code is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
8 # License as published by the Free Software Foundation; either
9 # version 3 of the License, or any later version.
10 #
11 # This code is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU AFFERO GENERAL PUBLIC LICENSE for more details.
15 #
16 # You should have received a copy of the GNU Affero General Public
17 # License along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 #
19 import functools
20 
21 import logging
22 
23 import datetime
24 
25 from privacyidea.lib.config import get_from_config
26 from privacyidea.models import UserCache, db
27 from sqlalchemy import and_
28 
29 log = logging.getLogger(__name__)
30 EXPIRATION_SECONDS = "UserCacheExpiration"
31 
32 
33 class user_cache(object):
34     """
35     This is the decorator wrapper to call a specific resolver function to
36     allow user caching.
37     If the user cache is disabled, the decorator wrapper is not called and
38     the original function's result is returned instead.
39     """
40 
41     def __init__(self, decorator_function):
42         """
43         :param decorator_function: This is the cache function that is to be
44             called
45         :type decorator_function: function
46         """
47         self.decorator_function = decorator_function
48 
49     def __call__(self, wrapped_function):
50         """
51         This decorates the given function.
52         The returned wrapper checks if the cache is enabled. If it is disabled, the
53         original function is called.
54 
55         :param wrapped_function: The function, that is decorated.
56         :return: None
57         """
58         @functools.wraps(wrapped_function)
59         def cache_wrapper(*args, **kwds):
60             if is_cache_enabled():
61                 return self.decorator_function(wrapped_function, *args, **kwds)
62             else:
63                 return wrapped_function(*args, **kwds)
64 
65         return cache_wrapper
66 
67 
68 def get_cache_time():
69     """
70     :return: UserCacheExpiration config value as a timedelta
71     :rtype: timedelta
72     """
73     seconds = int(get_from_config(EXPIRATION_SECONDS, '0'))
74     return datetime.timedelta(seconds=seconds)
75 
76 
77 def is_cache_enabled():
78     """
79     :return: True if the user cache is enabled (i.e. UserCacheExpiration is non-zero)
80     """
81     return bool(get_cache_time())
82 
83 
84 def delete_user_cache(resolver=None, username=None, expired=None):
85     """
86     This completely deletes the user cache.
87     If no parameter is given, it deletes the user cache completely.
88 
89     :param resolver: Will only delete entries of this resolver
90     :param username: Will only delete entries of this username
91     :param expired: Will delete expired (True) or non-expired (False) entries
92         or will not care about the expiration date (None)
93 
94     :return: number of deleted entries
95     :rtype: int
96     """
97     filter_condition = create_filter(username=username, resolver=resolver,
98                                      expired=expired)
99     rowcount = db.session.query(UserCache).filter(filter_condition).delete()
100     db.session.commit()
101     log.info(u'Deleted {} entries from the user cache (resolver={!r}, username={!r}, expired={!r})'.format(
102         rowcount, resolver, username, expired
103     ))
104     return rowcount
105 
106 
107 def add_to_cache(username, used_login, resolver, user_id):
108     """
109     Add the given record to the user cache, if it is enabled.
110     The user cache is considered disabled if the config option
111     EXPIRATION_SECONDS is set to 0.
112     :param username: login name of the user
113     :param used_login: login name that was used in request
114     :param resolver: resolver name of the user
115     :param user_id: ID of the user in its resolver
116     """
117     if is_cache_enabled():
118         timestamp = datetime.datetime.now()
119         record = UserCache(username, used_login, resolver, user_id, timestamp)
120         log.debug(u'Adding record to cache: ({!r}, {!r}, {!r}, {!r}, {!r})'.format(
121             username, used_login, resolver, user_id, timestamp))
122         record.save()
123 
124 
125 def retrieve_latest_entry(filter_condition):
126     """
127     Return the most recently added entry in the user cache matching the given filter condition, or None.
128     :param filter_condition: SQLAlchemy filter, as created (for example) by create_filter
129     :return: A `UserCache` object or None, if no entry matches the given condition.
130     """
131     return UserCache.query.filter(filter_condition).order_by(UserCache.timestamp.desc()).first()
132 
133 
134 def create_filter(username=None, used_login=None, resolver=None,
135                   user_id=None, expired=False):
136     """
137     Build and return a SQLAlchemy query that searches the UserCache cache for a combination
138     of username, resolver and user ID. This also takes the expiration time into account.
139 
140     :param username: will filter for username
141     :param used_login: will filter for used_login
142     :param resolver: will filter for this resolver name
143     :param user_id: will filter for this user ID
144     :param expired: Can be True/False/None. If set to False will return
145         only non-expired entries. If set to True, it will return only expired entries.
146         If set to None, it will return expired as well as non-expired entries.
147 
148     :return: SQLAlchemy Filter
149     """
150     conditions = []
151     if expired:
152         cache_time = get_cache_time()
153         conditions.append(
154             UserCache.timestamp < datetime.datetime.now() - cache_time)
155     elif expired is False:
156         cache_time = get_cache_time()
157         conditions.append(UserCache.timestamp >= datetime.datetime.now() - cache_time)
158 
159     if username:
160         conditions.append(UserCache.username == username)
161     if used_login:
162         conditions.append(UserCache.used_login == used_login)
163     if resolver:
164         conditions.append(UserCache.resolver == resolver)
165     if user_id:
166         conditions.append(UserCache.user_id == user_id)
167     filter_condition = and_(*conditions)
168     return filter_condition
169 
170 
171 def cache_username(wrapped_function, userid, resolvername):
172     """
173     Decorator that adds a UserCache lookup to a function that looks up user
174     names based on a user ID and a resolver name.
175     After a successful lookup, the entry is added to the cache.
176     """
177 
178     # try to fetch the record from the UserCache
179     filter_conditions = create_filter(user_id=userid,
180                                       resolver=resolvername)
181     result = retrieve_latest_entry(filter_conditions)
182     if result:
183         username = result.username
184         log.debug(u'Found username of {!r}/{!r} in cache: {!r}'.format(userid, resolvername, username))
185         return username
186     else:
187         # record was not found in the cache
188         username = wrapped_function(userid, resolvername)
189         if username:
190             # If we could figure out a user name, add the record to the cache.
191             add_to_cache(username, username, resolvername, userid)
192         return username
193 
194 
195 def user_init(wrapped_function, self):
196     """
197     Decorator to decorate the User creation function
198 
199     :param wrapped_function:
200     :param self:
201     :return:
202     """
203     if self.resolver:
204         resolvers = [self.resolver]
205     else:
206         # In order to query the user cache, we need to find out the resolver
207         resolvers = self.get_ordererd_resolvers()
208     for resolvername in resolvers:
209         # If we could figure out a resolver, we can query the user cache
210         filter_conditions = create_filter(used_login=self.used_login, resolver=resolvername)
211         result = retrieve_latest_entry(filter_conditions)
212         if result:
213             # Cached user exists, retrieve information and exit early
214             self.login = result.username
215             self.resolver = result.resolver
216             self.uid = result.user_id
217             return
218         else:
219             # If the user does not exist in the cache, we actually query the resolver
220             # before moving on to the next resolver in the prioritized list.
221             # This is important in the following scenario:
222             # We have a realm with two resolvers, resolverA and resolverB,
223             # a user foo has been located in resolverB and a corresponding cache entry
224             # has been added.
225             # Now, if a user of the same name is added to the store associated with resolverA,
226             # we want to notice! This is why we actually query resolverA if there is no
227             # cache entry associating foo with resolverA before checking the cache entries
228             # of resolverB. Otherwise, we could end up with a cache that associates foo with
229             # resolverB even though it should be associated with resolverA.
230             if self._locate_user_in_resolver(resolvername):
231                 break
232     # Either we could not determine a resolver or we could, but the user is not in cache.
233     # We need to get additional information from the userstore.
234     wrapped_function(self)
235     # If the user object is complete, add it to the cache.
236     if self.login and self.resolver and self.uid and self.used_login:
237         # We only cache complete sets!
238         add_to_cache(self.login, self.used_login, self.resolver, self.uid)
239 
240
```
--- 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()

```

diff --git a/privacyidea/lib/usercache.py b/privacyidea/lib/usercache.py
--- a/privacyidea/lib/usercache.py
+++ b/privacyidea/lib/usercache.py
@@ -70,7 +70,12 @@
     :return: UserCacheExpiration config value as a timedelta
     :rtype: timedelta
     """
-    seconds = int(get_from_config(EXPIRATION_SECONDS, '0'))
+    seconds = 0
+    try:
+        seconds = int(get_from_config(EXPIRATION_SECONDS, '0'))
+    except ValueError:
+        log.info(u"Non-Integer value stored in system config {0!s}".format(EXPIRATION_SECONDS))
+
     return datetime.timedelta(seconds=seconds)

{"golden_diff": "diff --git a/privacyidea/lib/usercache.py b/privacyidea/lib/usercache.py\n--- a/privacyidea/lib/usercache.py\n+++ b/privacyidea/lib/usercache.py\n@@ -70,7 +70,12 @@\n     :return: UserCacheExpiration config value as a timedelta\n     :rtype: timedelta\n     \"\"\"\n-    seconds = int(get_from_config(EXPIRATION_SECONDS, '0'))\n+    seconds = 0\n+    try:\n+        seconds = int(get_from_config(EXPIRATION_SECONDS, '0'))\n+    except ValueError:\n+        log.info(u\"Non-Integer value stored in system config {0!s}\".format(EXPIRATION_SECONDS))\n+\n     return datetime.timedelta(seconds=seconds)\n", "issue": "Setting and then Clearing 'User Cache expiration in seconds' Breaks Authentication\n# What did you try to do?\r\n\r\nClear/delete value for 'User Cache expiration in seconds' under System Config.\r\n\r\n# What outcome did you expect?\r\n\r\nValue reset to default of 0.\r\n\r\n# What outcome did you experience?\r\n\r\nAuthentication breaks after value is cleared.  Error is logged to log file.\r\n\r\n`\r\n[2019-04-26 14:31:42,511][ERROR][privacyidea.lib.token:493] User information can not be retrieved: invalid literal for int() with base 10: ''\r\n`\r\n\r\nChanging UserCacheExpiration back to 0 directly in the config table in the pi database and restarting the Apache2 service resolves the issue.\r\n\r\n# Configuration\r\n\r\n* privacyIDEA Version: 3.0\r\n* Installation method: ubuntu packages\r\n\r\n## more details: \r\n\r\n* OS: Ubuntu 16.04LTS\r\n* Webserver: Apache2\r\n* Tokendatabase: MySQL\r\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n#\n#  2017-03-30   Friedrich Weber <friedrich.weber@netknights.it>\n#               First ideas for a user cache to improve performance\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 FITNESS 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 functools\n\nimport logging\n\nimport datetime\n\nfrom privacyidea.lib.config import get_from_config\nfrom privacyidea.models import UserCache, db\nfrom sqlalchemy import and_\n\nlog = logging.getLogger(__name__)\nEXPIRATION_SECONDS = \"UserCacheExpiration\"\n\n\nclass user_cache(object):\n    \"\"\"\n    This is the decorator wrapper to call a specific resolver function to\n    allow user caching.\n    If the user cache is disabled, the decorator wrapper is not called and\n    the original function's result is returned instead.\n    \"\"\"\n\n    def __init__(self, decorator_function):\n        \"\"\"\n        :param decorator_function: This is the cache function that is to be\n            called\n        :type decorator_function: function\n        \"\"\"\n        self.decorator_function = decorator_function\n\n    def __call__(self, wrapped_function):\n        \"\"\"\n        This decorates the given function.\n        The returned wrapper checks if the cache is enabled. If it is disabled, the\n        original function is called.\n\n        :param wrapped_function: The function, that is decorated.\n        :return: None\n        \"\"\"\n        @functools.wraps(wrapped_function)\n        def cache_wrapper(*args, **kwds):\n            if is_cache_enabled():\n                return self.decorator_function(wrapped_function, *args, **kwds)\n            else:\n                return wrapped_function(*args, **kwds)\n\n        return cache_wrapper\n\n\ndef get_cache_time():\n    \"\"\"\n    :return: UserCacheExpiration config value as a timedelta\n    :rtype: timedelta\n    \"\"\"\n    seconds = int(get_from_config(EXPIRATION_SECONDS, '0'))\n    return datetime.timedelta(seconds=seconds)\n\n\ndef is_cache_enabled():\n    \"\"\"\n    :return: True if the user cache is enabled (i.e. UserCacheExpiration is non-zero)\n    \"\"\"\n    return bool(get_cache_time())\n\n\ndef delete_user_cache(resolver=None, username=None, expired=None):\n    \"\"\"\n    This completely deletes the user cache.\n    If no parameter is given, it deletes the user cache completely.\n\n    :param resolver: Will only delete entries of this resolver\n    :param username: Will only delete entries of this username\n    :param expired: Will delete expired (True) or non-expired (False) entries\n        or will not care about the expiration date (None)\n\n    :return: number of deleted entries\n    :rtype: int\n    \"\"\"\n    filter_condition = create_filter(username=username, resolver=resolver,\n                                     expired=expired)\n    rowcount = db.session.query(UserCache).filter(filter_condition).delete()\n    db.session.commit()\n    log.info(u'Deleted {} entries from the user cache (resolver={!r}, username={!r}, expired={!r})'.format(\n        rowcount, resolver, username, expired\n    ))\n    return rowcount\n\n\ndef add_to_cache(username, used_login, resolver, user_id):\n    \"\"\"\n    Add the given record to the user cache, if it is enabled.\n    The user cache is considered disabled if the config option\n    EXPIRATION_SECONDS is set to 0.\n    :param username: login name of the user\n    :param used_login: login name that was used in request\n    :param resolver: resolver name of the user\n    :param user_id: ID of the user in its resolver\n    \"\"\"\n    if is_cache_enabled():\n        timestamp = datetime.datetime.now()\n        record = UserCache(username, used_login, resolver, user_id, timestamp)\n        log.debug(u'Adding record to cache: ({!r}, {!r}, {!r}, {!r}, {!r})'.format(\n            username, used_login, resolver, user_id, timestamp))\n        record.save()\n\n\ndef retrieve_latest_entry(filter_condition):\n    \"\"\"\n    Return the most recently added entry in the user cache matching the given filter condition, or None.\n    :param filter_condition: SQLAlchemy filter, as created (for example) by create_filter\n    :return: A `UserCache` object or None, if no entry matches the given condition.\n    \"\"\"\n    return UserCache.query.filter(filter_condition).order_by(UserCache.timestamp.desc()).first()\n\n\ndef create_filter(username=None, used_login=None, resolver=None,\n                  user_id=None, expired=False):\n    \"\"\"\n    Build and return a SQLAlchemy query that searches the UserCache cache for a combination\n    of username, resolver and user ID. This also takes the expiration time into account.\n\n    :param username: will filter for username\n    :param used_login: will filter for used_login\n    :param resolver: will filter for this resolver name\n    :param user_id: will filter for this user ID\n    :param expired: Can be True/False/None. If set to False will return\n        only non-expired entries. If set to True, it will return only expired entries.\n        If set to None, it will return expired as well as non-expired entries.\n\n    :return: SQLAlchemy Filter\n    \"\"\"\n    conditions = []\n    if expired:\n        cache_time = get_cache_time()\n        conditions.append(\n            UserCache.timestamp < datetime.datetime.now() - cache_time)\n    elif expired is False:\n        cache_time = get_cache_time()\n        conditions.append(UserCache.timestamp >= datetime.datetime.now() - cache_time)\n\n    if username:\n        conditions.append(UserCache.username == username)\n    if used_login:\n        conditions.append(UserCache.used_login == used_login)\n    if resolver:\n        conditions.append(UserCache.resolver == resolver)\n    if user_id:\n        conditions.append(UserCache.user_id == user_id)\n    filter_condition = and_(*conditions)\n    return filter_condition\n\n\ndef cache_username(wrapped_function, userid, resolvername):\n    \"\"\"\n    Decorator that adds a UserCache lookup to a function that looks up user\n    names based on a user ID and a resolver name.\n    After a successful lookup, the entry is added to the cache.\n    \"\"\"\n\n    # try to fetch the record from the UserCache\n    filter_conditions = create_filter(user_id=userid,\n                                      resolver=resolvername)\n    result = retrieve_latest_entry(filter_conditions)\n    if result:\n        username = result.username\n        log.debug(u'Found username of {!r}/{!r} in cache: {!r}'.format(userid, resolvername, username))\n        return username\n    else:\n        # record was not found in the cache\n        username = wrapped_function(userid, resolvername)\n        if username:\n            # If we could figure out a user name, add the record to the cache.\n            add_to_cache(username, username, resolvername, userid)\n        return username\n\n\ndef user_init(wrapped_function, self):\n    \"\"\"\n    Decorator to decorate the User creation function\n\n    :param wrapped_function:\n    :param self:\n    :return:\n    \"\"\"\n    if self.resolver:\n        resolvers = [self.resolver]\n    else:\n        # In order to query the user cache, we need to find out the resolver\n        resolvers = self.get_ordererd_resolvers()\n    for resolvername in resolvers:\n        # If we could figure out a resolver, we can query the user cache\n        filter_conditions = create_filter(used_login=self.used_login, resolver=resolvername)\n        result = retrieve_latest_entry(filter_conditions)\n        if result:\n            # Cached user exists, retrieve information and exit early\n            self.login = result.username\n            self.resolver = result.resolver\n            self.uid = result.user_id\n            return\n        else:\n            # If the user does not exist in the cache, we actually query the resolver\n            # before moving on to the next resolver in the prioritized list.\n            # This is important in the following scenario:\n            # We have a realm with two resolvers, resolverA and resolverB,\n            # a user foo has been located in resolverB and a corresponding cache entry\n            # has been added.\n            # Now, if a user of the same name is added to the store associated with resolverA,\n            # we want to notice! This is why we actually query resolverA if there is no\n            # cache entry associating foo with resolverA before checking the cache entries\n            # of resolverB. Otherwise, we could end up with a cache that associates foo with\n            # resolverB even though it should be associated with resolverA.\n            if self._locate_user_in_resolver(resolvername):\n                break\n    # Either we could not determine a resolver or we could, but the user is not in cache.\n    # We need to get additional information from the userstore.\n    wrapped_function(self)\n    # If the user object is complete, add it to the cache.\n    if self.login and self.resolver and self.uid and self.used_login:\n        # We only cache complete sets!\n        add_to_cache(self.login, self.used_login, self.resolver, self.uid)\n\n", "path": "privacyidea/lib/usercache.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n#\n#  2017-03-30   Friedrich Weber <friedrich.weber@netknights.it>\n#               First ideas for a user cache to improve performance\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 FITNESS 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 functools\n\nimport logging\n\nimport datetime\n\nfrom privacyidea.lib.config import get_from_config\nfrom privacyidea.models import UserCache, db\nfrom sqlalchemy import and_\n\nlog = logging.getLogger(__name__)\nEXPIRATION_SECONDS = \"UserCacheExpiration\"\n\n\nclass user_cache(object):\n    \"\"\"\n    This is the decorator wrapper to call a specific resolver function to\n    allow user caching.\n    If the user cache is disabled, the decorator wrapper is not called and\n    the original function's result is returned instead.\n    \"\"\"\n\n    def __init__(self, decorator_function):\n        \"\"\"\n        :param decorator_function: This is the cache function that is to be\n            called\n        :type decorator_function: function\n        \"\"\"\n        self.decorator_function = decorator_function\n\n    def __call__(self, wrapped_function):\n        \"\"\"\n        This decorates the given function.\n        The returned wrapper checks if the cache is enabled. If it is disabled, the\n        original function is called.\n\n        :param wrapped_function: The function, that is decorated.\n        :return: None\n        \"\"\"\n        @functools.wraps(wrapped_function)\n        def cache_wrapper(*args, **kwds):\n            if is_cache_enabled():\n                return self.decorator_function(wrapped_function, *args, **kwds)\n            else:\n                return wrapped_function(*args, **kwds)\n\n        return cache_wrapper\n\n\ndef get_cache_time():\n    \"\"\"\n    :return: UserCacheExpiration config value as a timedelta\n    :rtype: timedelta\n    \"\"\"\n    seconds = 0\n    try:\n        seconds = int(get_from_config(EXPIRATION_SECONDS, '0'))\n    except ValueError:\n        log.info(u\"Non-Integer value stored in system config {0!s}\".format(EXPIRATION_SECONDS))\n\n    return datetime.timedelta(seconds=seconds)\n\n\ndef is_cache_enabled():\n    \"\"\"\n    :return: True if the user cache is enabled (i.e. UserCacheExpiration is non-zero)\n    \"\"\"\n    return bool(get_cache_time())\n\n\ndef delete_user_cache(resolver=None, username=None, expired=None):\n    \"\"\"\n    This completely deletes the user cache.\n    If no parameter is given, it deletes the user cache completely.\n\n    :param resolver: Will only delete entries of this resolver\n    :param username: Will only delete entries of this username\n    :param expired: Will delete expired (True) or non-expired (False) entries\n        or will not care about the expiration date (None)\n\n    :return: number of deleted entries\n    :rtype: int\n    \"\"\"\n    filter_condition = create_filter(username=username, resolver=resolver,\n                                     expired=expired)\n    rowcount = db.session.query(UserCache).filter(filter_condition).delete()\n    db.session.commit()\n    log.info(u'Deleted {} entries from the user cache (resolver={!r}, username={!r}, expired={!r})'.format(\n        rowcount, resolver, username, expired\n    ))\n    return rowcount\n\n\ndef add_to_cache(username, used_login, resolver, user_id):\n    \"\"\"\n    Add the given record to the user cache, if it is enabled.\n    The user cache is considered disabled if the config option\n    EXPIRATION_SECONDS is set to 0.\n    :param username: login name of the user\n    :param used_login: login name that was used in request\n    :param resolver: resolver name of the user\n    :param user_id: ID of the user in its resolver\n    \"\"\"\n    if is_cache_enabled():\n        timestamp = datetime.datetime.now()\n        record = UserCache(username, used_login, resolver, user_id, timestamp)\n        log.debug(u'Adding record to cache: ({!r}, {!r}, {!r}, {!r}, {!r})'.format(\n            username, used_login, resolver, user_id, timestamp))\n        record.save()\n\n\ndef retrieve_latest_entry(filter_condition):\n    \"\"\"\n    Return the most recently added entry in the user cache matching the given filter condition, or None.\n    :param filter_condition: SQLAlchemy filter, as created (for example) by create_filter\n    :return: A `UserCache` object or None, if no entry matches the given condition.\n    \"\"\"\n    return UserCache.query.filter(filter_condition).order_by(UserCache.timestamp.desc()).first()\n\n\ndef create_filter(username=None, used_login=None, resolver=None,\n                  user_id=None, expired=False):\n    \"\"\"\n    Build and return a SQLAlchemy query that searches the UserCache cache for a combination\n    of username, resolver and user ID. This also takes the expiration time into account.\n\n    :param username: will filter for username\n    :param used_login: will filter for used_login\n    :param resolver: will filter for this resolver name\n    :param user_id: will filter for this user ID\n    :param expired: Can be True/False/None. If set to False will return\n        only non-expired entries. If set to True, it will return only expired entries.\n        If set to None, it will return expired as well as non-expired entries.\n\n    :return: SQLAlchemy Filter\n    \"\"\"\n    conditions = []\n    if expired:\n        cache_time = get_cache_time()\n        conditions.append(\n            UserCache.timestamp < datetime.datetime.now() - cache_time)\n    elif expired is False:\n        cache_time = get_cache_time()\n        conditions.append(UserCache.timestamp >= datetime.datetime.now() - cache_time)\n\n    if username:\n        conditions.append(UserCache.username == username)\n    if used_login:\n        conditions.append(UserCache.used_login == used_login)\n    if resolver:\n        conditions.append(UserCache.resolver == resolver)\n    if user_id:\n        conditions.append(UserCache.user_id == user_id)\n    filter_condition = and_(*conditions)\n    return filter_condition\n\n\ndef cache_username(wrapped_function, userid, resolvername):\n    \"\"\"\n    Decorator that adds a UserCache lookup to a function that looks up user\n    names based on a user ID and a resolver name.\n    After a successful lookup, the entry is added to the cache.\n    \"\"\"\n\n    # try to fetch the record from the UserCache\n    filter_conditions = create_filter(user_id=userid,\n                                      resolver=resolvername)\n    result = retrieve_latest_entry(filter_conditions)\n    if result:\n        username = result.username\n        log.debug(u'Found username of {!r}/{!r} in cache: {!r}'.format(userid, resolvername, username))\n        return username\n    else:\n        # record was not found in the cache\n        username = wrapped_function(userid, resolvername)\n        if username:\n            # If we could figure out a user name, add the record to the cache.\n            add_to_cache(username, username, resolvername, userid)\n        return username\n\n\ndef user_init(wrapped_function, self):\n    \"\"\"\n    Decorator to decorate the User creation function\n\n    :param wrapped_function:\n    :param self:\n    :return:\n    \"\"\"\n    if self.resolver:\n        resolvers = [self.resolver]\n    else:\n        # In order to query the user cache, we need to find out the resolver\n        resolvers = self.get_ordererd_resolvers()\n    for resolvername in resolvers:\n        # If we could figure out a resolver, we can query the user cache\n        filter_conditions = create_filter(used_login=self.used_login, resolver=resolvername)\n        result = retrieve_latest_entry(filter_conditions)\n        if result:\n            # Cached user exists, retrieve information and exit early\n            self.login = result.username\n            self.resolver = result.resolver\n            self.uid = result.user_id\n            return\n        else:\n            # If the user does not exist in the cache, we actually query the resolver\n            # before moving on to the next resolver in the prioritized list.\n            # This is important in the following scenario:\n            # We have a realm with two resolvers, resolverA and resolverB,\n            # a user foo has been located in resolverB and a corresponding cache entry\n            # has been added.\n            # Now, if a user of the same name is added to the store associated with resolverA,\n            # we want to notice! This is why we actually query resolverA if there is no\n            # cache entry associating foo with resolverA before checking the cache entries\n            # of resolverB. Otherwise, we could end up with a cache that associates foo with\n            # resolverB even though it should be associated with resolverA.\n            if self._locate_user_in_resolver(resolvername):\n                break\n    # Either we could not determine a resolver or we could, but the user is not in cache.\n    # We need to get additional information from the userstore.\n    wrapped_function(self)\n    # If the user object is complete, add it to the cache.\n    if self.login and self.resolver and self.uid and self.used_login:\n        # We only cache complete sets!\n        add_to_cache(self.login, self.used_login, self.resolver, self.uid)\n\n", "path": "privacyidea/lib/usercache.py"}]}