def wrapper(*args): # Invariant: cache is sorted by timeout # Requirement: time.time is a monotonically increasing function c = self.conn.cursor() # Generate Key hash_args = args[1:] if self.ignore_environ else args[:] key = hmac.new(self.salt, encode_value(hash_args), digestmod=hashlib.sha256).hexdigest() # Cache Lookup found = False c.execute("SELECT value FROM cache WHERE key = ? AND expiration > ? LIMIT 1;", (key, time.time())) for row in c: result = decode_value(row[0]) found = True # Query Function if not found: result = f(*args) if not self.cache_true_only or (self.cache_true_only and result is True): timeout = time.time() + self.timeout try: c.execute("INSERT INTO cache(key, value, expiration) VALUES (?, ?, ?);", (key, encode_value(result), timeout)) except sqlite3.IntegrityError: c.execute("UPDATE cache SET expiration = ? WHERE key = ?;", (timeout, key)) self.conn.commit() return result
def __init__(self, filename, **options): # Options self.timeout = options.get('timeout', 60*30) # 30 minutes self.ignore_environ = options.get('ignore_environ', True) # by default, ignore the environment variables self.cache_true_only = options.get('cache_true_only', True) self.salt = None # Connect to the database if not filename == ':memory:': filename = self._normalize_filename(filename) # TODO: Enforce 0600 perms on UNIX? self.conn = sqlite3.connect(filename) c = self.conn.cursor() attempts = 0 while self.salt is None and attempts <= 1: try: c.execute("CREATE TABLE cache (key TEXT UNIQUE, value TEXT, expiration INTEGER);") except sqlite3.OperationalError: # Cache table exists c.execute('DELETE FROM cache WHERE expiration < ?;', (time.time(),)) c.execute("SELECT value FROM cache WHERE key = ? LIMIT 1;", (Cache.SALT_NAME, )) for row in c: self.salt = decode_value(row[0]) else: # Cache table just created salt = os.urandom(hmac.HMAC.blocksize) # Default: 512-bit HMAC c.execute("CREATE INDEX IF NOT EXISTS expiration ON cache (expiration ASC);") c.execute('INSERT INTO cache(key, value) VALUES (?, ?);', (Cache.SALT_NAME, encode_value(salt))) finally: self.conn.commit() attempts += 1 assert self.salt is not None, "wsgi_googleauth Error: Salt not initialized"