def importBlobFromViur2(dlKey, fileName): if not conf.get("viur.viur2import.blobsource"): return False existingImport = db.Get(db.Key("viur-viur2-blobimport", dlKey)) if existingImport: if existingImport["success"]: return existingImport["dlurl"] return False if conf["viur.viur2import.blobsource"]["infoURL"]: try: importDataReq = urlopen( conf["viur.viur2import.blobsource"]["infoURL"] + dlKey) except: marker = db.Entity(db.Key("viur-viur2-blobimport", dlKey)) marker["success"] = False marker["error"] = "Failed URL-FETCH 1" db.Put(marker) return False if importDataReq.status != 200: marker = db.Entity(db.Key("viur-viur2-blobimport", dlKey)) marker["success"] = False marker["error"] = "Failed URL-FETCH 2" db.Put(marker) return False importData = json.loads(importDataReq.read()) oldBlobName = conf["viur.viur2import.blobsource"][ "gsdir"] + "/" + importData["key"] srcBlob = storage.Blob( bucket=bucket, name=conf["viur.viur2import.blobsource"]["gsdir"] + "/" + importData["key"]) else: oldBlobName = conf["viur.viur2import.blobsource"]["gsdir"] + "/" + dlKey srcBlob = storage.Blob( bucket=bucket, name=conf["viur.viur2import.blobsource"]["gsdir"] + "/" + dlKey) if not srcBlob.exists(): marker = db.Entity(db.Key("viur-viur2-blobimport", dlKey)) marker["success"] = False marker["error"] = "Local SRC-Blob missing" marker["oldBlobName"] = oldBlobName db.Put(marker) return False bucket.rename_blob(srcBlob, "%s/source/%s" % (dlKey, fileName)) marker = db.Entity(db.Key("viur-viur2-blobimport", dlKey)) marker["success"] = True marker["old_src_key"] = dlKey marker["old_src_name"] = fileName marker["dlurl"] = utils.downloadUrlFor(dlKey, fileName, False, None) db.Put(marker) return marker["dlurl"]
def view(self, *args, **kwargs): """ Prepares and renders the singleton entry for viewing. The function performs several access control checks on the requested entity before it is rendered. .. seealso:: :func:`viewSkel`, :func:`canView`, :func:`onViewed` :returns: The rendered representation of the entity. :raises: :exc:`server.errors.NotFound`, if there is no singleton entry existing, yet. :raises: :exc:`server.errors.Unauthorized`, if the current user does not have the required permissions. """ skel = self.viewSkel() if not self.canView(): raise errors.Unauthorized() key = db.Key(self.editSkel().kindName, self.getKey()) if not skel.fromDB(key): raise errors.NotFound() self.onViewed(skel) return self.render.view(skel)
def validate(key: str, useSessionKey: bool) -> Union[bool, db.Entity]: """ Validates a onetime securitykey :type key: str :param key: The key to validate :type useSessionKey: Bool :param useSessionKey: If True, we validate against the session's skey, otherwise we'll lookup an unbound key :returns: False if the key was not valid for whatever reasons, the data (given during createSecurityKey) as dictionary or True if the dict is empty. """ if useSessionKey: if key == "staticSessionKey": skeyHeaderValue = currentRequest.get().request.headers.get( "Sec-X-ViUR-StaticSKey") if skeyHeaderValue and currentSession.get( ).validateStaticSecurityKey(skeyHeaderValue): return True elif currentSession.get().validateSecurityKey(key): return True return False if not key: return False dbKey = db.Key(securityKeyKindName, key) dbObj = db.Get(dbKey) if dbObj: db.Delete(dbKey) if dbObj["until"] < datetime.now(): # This key has expired return False del dbObj["until"] if not dbObj: return True return dbObj return False
def storeEntry2(self, e, key): if not self._checkKey(key, export=False): raise errors.Forbidden() entry = pickle.loads(e.decode("HEX")) if not "key" in entry and "id" in entry: entry["key"] = entry["id"] for k in list(entry.keys())[:]: if isinstance(entry[k], str): entry[k] = entry[k].decode("UTF-8") key = db.Key(encoded=utils.normalizeKey(entry["key"])) dbEntry = db.Entity(kind=key.kind(), parent=key.parent(), id=key.id(), name=key.name()) # maybe some more fixes here ? for k in entry.keys(): if k != "key": val = entry[k] dbEntry[k] = val db.Put(dbEntry) if dbEntry.key().id(): # Ensure the Datastore knows that it's id is in use datastore._GetConnection()._reserve_keys([dbEntry.key()]) try: skel = skeletonByKind(key.kind())() except: logging.error("Unknown Skeleton - skipping") skel.fromDB(str(dbEntry.key())) skel.refresh() skel.toDB(clearUpdateTag=True)
def setKeyTxn(orderKey, idx): """Assigns the new order to the given order""" dbObj = db.Get(db.Key(orderKey)) if not dbObj: return dbObj["idx"] = idx db.Put(dbObj)
def doCheckForUnreferencedBlobs(cursor=None): def getOldBlobKeysTxn(dbKey): obj = db.Get(dbKey) res = obj["old_blob_references"] or [] if obj["is_stale"]: db.Delete(dbKey) else: obj["has_old_blob_references"] = False obj["old_blob_references"] = [] db.Put(obj) return res query = db.Query("viur-blob-locks").filter("has_old_blob_references", True).setCursor(cursor) for lockObj in query.run(100): oldBlobKeys = db.RunInTransaction(getOldBlobKeysTxn, lockObj.key) for blobKey in oldBlobKeys: if db.Query("viur-blob-locks").filter("active_blob_references =", blobKey).getEntry(): # This blob is referenced elsewhere logging.info("Stale blob is still referenced, %s" % blobKey) continue # Add a marker and schedule it for deletion fileObj = db.Query("viur-deleted-files").filter("dlkey", blobKey).getEntry() if fileObj: # Its already marked logging.info("Stale blob already marked for deletion, %s" % blobKey) return fileObj = db.Entity(db.Key("viur-deleted-files")) fileObj["itercount"] = 0 fileObj["dlkey"] = str(blobKey) logging.info("Stale blob marked dirty, %s" % blobKey) db.Put(fileObj) newCursor = query.getCursor() if newCursor: doCheckForUnreferencedBlobs(newCursor)
def assignBillSequence(self, orderKey): """ Assigns an unique order-order to the given order. """ def getKeyTxn(kindName, orderKey): """Generates and returns a new, unique Key""" seqObj = db.GetOrInsert(kindName, "viur_bill_sequences", count=1000) idx = seqObj["count"] seqObj["count"] += 1 db.Put(seqObj) return str(idx) def setKeyTxn(orderKey, idx): """Assigns the new order to the given order""" dbObj = db.Get(db.Key(orderKey)) if not dbObj: return dbObj["idx"] = idx db.Put(dbObj) dbObj = db.Get(db.Key(orderKey)) if not dbObj: return idx = db.RunInTransaction(getKeyTxn, self.viewSkel().kindName, orderKey) db.RunInTransaction(setKeyTxn, orderKey, idx) self.billSequenceAvailable(orderKey)
def sofortStatus(self, *args, **kwargs): sortOrder = [ "transaction", "user_id", "project_id", "sender_holder", "sender_account_number", "sender_bank_code", "sender_bank_name", "sender_bank_bic", "sender_iban", "sender_country_id", "recipient_holder", "recipient_account_number", "recipient_bank_code", "recipient_bank_name", "recipient_bank_bic", "recipient_iban", "recipient_country_id", "international_transaction", "amount", "currency_id", "reason_1", "reason_2", "security_criteria", "user_variable_0", "user_variable_1", "user_variable_2", "user_variable_3", "user_variable_4", "user_variable_5", "created" ] hashstr = "|".join([kwargs[key] for key in sortOrder] + [conf["sofort"]["notificationpassword"]]) if hashlib.sha512( hashstr.encode("utf-8")).hexdigest() != kwargs["hash"]: logging.error( "RECEIVED INVALID HASH FOR sofort (%s!=%s)" % (hashlib.sha512( hashstr.encode("utf-8")).hexdigest(), kwargs["hash"])) return ("INVALID HASH") order = db.Get(db.Key(kwargs["user_variable_0"])) if not order: logging.error("RECEIVED UNKNOWN ORDER by sofort (%s)" % (kwargs["user_variable_0"])) return ("UNKNOWN ORDER") if ("%.2f" % order["price"]) != kwargs["amount"]: logging.error("RECEIVED INVALID AMOUNT PAYED sofort (%s!=%s)" % (order["price"], kwargs["amount"])) return ("INVALID AMOUNT") self.orderHandler.setPayed(kwargs["user_variable_0"]) return ("OKAY")
def validate(key: str, useSessionKey: bool) -> Union[bool, db.Entity]: """ Validates a security key. If useSessionKey is true, the key is expected to be the sessions current security key (or it's static security key). Otherwise it must be a key created with a duration (so it's not session dependent) :param key: The key to validate :param useSessionKey: If True, we validate against the session's skey, otherwise we'll lookup an unbound key :returns: False if the key was not valid for whatever reasons, the data (given during createSecurityKey) as dictionary or True if the dict is empty (or :param:useSessionKey was true). """ if useSessionKey: if key == "staticSessionKey": skeyHeaderValue = currentRequest.get().request.headers.get( "Sec-X-ViUR-StaticSKey") if skeyHeaderValue and currentSession.get( ).validateStaticSecurityKey(skeyHeaderValue): return True elif currentSession.get().validateSecurityKey(key): return True return False if not key: return False dbKey = db.Key(securityKeyKindName, key) dbObj = db.Get(dbKey) if dbObj: db.Delete(dbKey) until = dbObj["until"] if until < utcNow(): # This key has expired return False del dbObj["until"] if not dbObj: return True return dbObj return False
def load(self, req): """ Initializes the Session. If the client supplied a valid Cookie, the session is read from the memcache/datastore, otherwise a new, empty session will be initialized. """ self.changed = False self.isInitial = False self.cookieKey = None self.sslKey = None self.staticSecurityKey = None self.securityKey = None self.session = {} if self.cookieName in req.request.cookies: cookie = str(req.request.cookies[self.cookieName]) data = db.Get(db.Key(self.kindName, cookie)) if data: # Loaded successfully from Memcache if data["lastseen"] < time() - conf["viur.session.lifeTime"]: # This session is too old self.reset() return False self.session = data["data"] self.staticSecurityKey = data["staticSecurityKey"] self.securityKey = data["securityKey"] self.cookieKey = cookie if data["lastseen"] < time() - 5 * 60: # Refresh every 5 Minutes self.changed = True else: # We could not load from firebase; create a new one self.reset() else: self.reset()
def txn(orderKey, state, removeState): dbObj = db.Get(db.Key(orderKey)) if not dbObj: return dbObj["state_%s" % state] = "1" if not removeState else "0" dbObj["changedate"] = datetime.now() db.Put(dbObj)
def wrapF(self, *args, **kwargs) -> Union[str, bytes]: currReq = currentRequest.get() if conf["viur.disableCache"] or currReq.disableCache: # Caching disabled if conf["viur.disableCache"]: logging.debug("Caching is disabled by config") return f(self, *args, **kwargs) # How many arguments are part of the way to the function called (and how many are just *args) offset = -len(currReq.args) or len(currReq.pathlist) path = "/" + "/".join(currReq.pathlist[:offset]) if not path in urls: # This path (possibly a sub-render) should not be cached logging.debug("Not caching for %s" % path) return f(self, *args, **kwargs) key = keyFromArgs(f, userSensitive, languageSensitive, evaluatedArgs, path, args, kwargs) if not key: # Something is wrong (possibly the parameter-count) # Let's call f, but we knew already that this will clash return f(self, *args, **kwargs) dbRes = db.Get(db.Key(viurCacheName, key)) if dbRes is not None: if not maxCacheTime \ or dbRes["creationtime"] > utils.utcNow() - timedelta(seconds=maxCacheTime): # We store it unlimited or the cache is fresh enough logging.debug("This request was served from cache.") currReq.response.headers['Content-Type'] = dbRes[ "content-type"] return dbRes["data"] # If we made it this far, the request wasn't cached or too old; we need to rebuild it oldAccessLog = db.startDataAccessLog() try: res = f(self, *args, **kwargs) finally: accessedEntries = db.endDataAccessLog(oldAccessLog) dbEntity = db.Entity(db.Key(viurCacheName, key)) dbEntity["data"] = res dbEntity["creationtime"] = utils.utcNow() dbEntity["path"] = path dbEntity["content-type"] = currReq.response.headers['Content-Type'] dbEntity["accessedEntries"] = list(accessedEntries) dbEntity.exclude_from_indexes = ["data", "content-type" ] # We can save 2 DB-Writs :) db.Put(dbEntity) logging.debug("This request was a cache-miss. Cache has been updated.") return res
def exchangeSecurityKey(): dbSession = db.Get(db.Key(self.kindName, self.cookieKey)) if not dbSession: # Should not happen (except if session.reset has been called in the same request) return False if dbSession["securityKey"] != key: # Race-Condidtion: That skey has been used in another instance return False dbSession["securityKey"] = utils.generateRandomString(13) db.Put(dbSession) return dbSession["securityKey"]
def ensureOwnModuleRootNode(self) -> db.Entity: """ Ensures, that general root-node for the current module exists. If no root-node exists yet, it will be created. :returns: The entity of the root-node. """ key = "rep_module_repo" kindName = self.viewSkel("node").kindName return db.GetOrInsert(db.Key(kindName, key), creationdate=datetime.datetime.now(), rootNode=1)
def updateTxn(cacheKey): key = db.Key(self.rateLimitKind, cacheKey) obj = db.Get(key) if obj is None: obj = db.Entity(key) obj["value"] = 0 obj["value"] += 1 obj["expires"] = utils.utcNow() + timedelta(minutes=2 * self.minutes) db.Put(obj)
def getSofortURL(self, orderID): order = db.Get(db.Key(orderID)) hashstr = "%s|%s|||||%.2f|EUR|%s||%s||||||%s" % ( conf["sofort"]["userid"], conf["sofort"]["projectid"], float(order["price"]), str(order.key()), str( order.key()), conf["sofort"]["projectpassword"]) hash = hashlib.sha512(hashstr.encode("UTF-8")).hexdigest() returnURL = "https://www.sofortueberweisung.de/payment/start?user_id=%s&project_id=%s&amount=%.2f¤cy_id=EUR&reason_1=%s&user_variable_0=%s&hash=%s" % ( conf["sofort"]["userid"], conf["sofort"]["projectid"], float(order["price"]), str(order.key()), str(order.key()), hash) return (returnURL)
def flushCache(prefix: str = None, key: Union[db.Key, None] = None, kind: Union[str, None] = None): """ Flushes the cache. Its possible the flush only a part of the cache by specifying the path-prefix. The path is equal to the url that caused it to be cached (eg /page/view) and must be one listed in the 'url' param of :meth:`viur.core.cache.enableCache`. :param prefix: Path or prefix that should be flushed. :param key: Flush all cache entries which may contain this key. Also flushes entries which executed a query over that kind. :param kind: Flush all cache entries which executed a query over that kind. Examples: - "/" would flush the main page (and only that), - "/*" everything from the cache, "/page/*" everything from the page-module (default render), - and "/page/view/*" only that specific subset of the page-module. """ if prefix is None and key is None and kind is None: prefix = "/*" if prefix is not None: items = db.Query(viurCacheName).filter("path =", prefix.rstrip("*")).iter() for item in items: db.Delete(item) if prefix.endswith("*"): items = db.Query(viurCacheName) \ .filter("path >", prefix.rstrip("*")) \ .filter("path <", prefix.rstrip("*") + u"\ufffd") \ .iter() for item in items: db.Delete(item) logging.debug( "Flushing cache succeeded. Everything matching \"%s\" is gone." % prefix) if key is not None: items = db.Query(viurCacheName).filter("accessedEntries =", key).iter() for item in items: logging.info("Deleted cache entry %s", item["path"]) db.Delete(item.key) if not isinstance(key, db.Key): key = db.Key(encoded=key) items = db.Query(viurCacheName).filter("accessedEntries =", key.kind).iter() for item in items: logging.info("Deleted cache entry %s", item["path"]) db.Delete(item.key) if kind is not None: items = db.Query(viurCacheName).filter("accessedEntries =", kind).iter() for item in items: logging.info("Deleted cache entry %s", item["path"]) db.Delete(item.key)
def getContents(self): """ Returns the entity of this singleton application as :class:`server.skeleton.Skeleton` object. :returns: The content as Skeleton provided by :func:`viewSkel`. """ skel = self.viewSkel() key = db.Key(self.viewSkel().kindName, self.getKey()) if not skel.fromDB(key): return None return skel
def ensureOwnModuleRootNode(self): """ Ensures, that general root-node for the current module exists. If no root-node exists yet, it will be created. :returns: The entity of the root-node. :rtype: :class:`server.db.Entity` """ key = "rep_module_repo" kindName = self.viewSkel("node").kindName return db.GetOrInsert(db.Key(kindName, key), creationdate=utils.utcNow(), rootNode=1)
def _rewriteQuery(self, name, skel, dbFilter, rawFilter): """ Rewrites a datastore query to operate on "viur-relations" instead of the original kind. This is needed to perform relational queries on n:m relations. """ origFilter = dbFilter.filters origSortOrders = dbFilter.orders if isinstance(origFilter, list): raise NotImplementedError( "Doing a relational Query with multiple=True and \"IN or !=\"-filters is currently unsupported!") dbFilter.filters = {} dbFilter.kind = "viur-relations" dbFilter.filter("viur_src_kind =", skel.kindName) dbFilter.filter("viur_dest_kind =", self.kind) dbFilter.filter("viur_src_property", name) # FIXME vvvv # if dbFilter._origCursor: # Merge the cursor in again (if any) # dbFilter.cursor(dbFilter._origCursor) if origFilter: for k, v in origFilter.items(): # Merge old filters in # Ensure that all non-relational-filters are in parentKeys if k == db.KEY_SPECIAL_PROPERTY: # We must process the key-property separately as its meaning changes as we change the datastore kind were querying if isinstance(v, list) or isinstance(v, tuple): logging.warning( "Invalid filtering! Doing an relational Query on %s with multiple key= filters is unsupported!" % ( name)) raise RuntimeError() if not isinstance(v, db.Key): v = db.Key(v) dbFilter.ancestor(v) continue boneName = k.split(".")[0].split(" ")[0] if boneName not in self.parentKeys and boneName != "__key__": logging.warning( "Invalid filtering! %s is not in parentKeys of RelationalBone %s!" % (boneName, name)) raise RuntimeError() dbFilter.filter("src.%s" % k, v) orderList = [] for k, d in origSortOrders: # Merge old sort orders in if k == db.KEY_SPECIAL_PROPERTY: orderList.append(("%s" % k, d)) elif not k in self.parentKeys: logging.warning("Invalid filtering! %s is not in parentKeys of RelationalBone %s!" % (k, name)) raise RuntimeError() else: orderList.append(("src.%s" % k, d)) if orderList: dbFilter.order(*orderList) return name, skel, dbFilter, rawFilter
def save(self, req): """ Writes the session to the memcache/datastore. Does nothing, if the session hasn't been changed in the current request. """ try: if self.changed or self.isInitial: serialized = base64.b64encode( pickle.dumps(self.session, protocol=pickle.HIGHEST_PROTOCOL)) # Get the current user id try: # Check for our custom user-api userid = conf["viur.mainApp"].user.getCurrentUser()["key"] except: userid = None if self.isInitial and not req.isSSLConnection: # Reset the Secure only key to None - we can't set it anyway. self.sslKey = None try: dbSession = db.Entity(db.Key(self.kindName, self.httpKey)) dbSession["data"] = serialized dbSession["sslkey"] = self.sslKey dbSession["staticSecurityKey"] = self.staticSecurityKey dbSession["securityKey"] = self.securityKey dbSession["lastseen"] = time() # Store the userid inside the sessionobj, so we can kill specific sessions if needed dbSession["user"] = str(userid) or "guest" db.Put(dbSession) except Exception as e: logging.exception(e) raise # FIXME pass if self.sameSite: sameSite = "; SameSite=%s" % self.sameSite else: sameSite = "" req.response.headerlist.append( ("Set-Cookie", "%s=%s; Max-Age=99999; Path=/; HttpOnly%s" % (self.plainCookieName, self.httpKey, sameSite))) if req.isSSLConnection: req.response.headerlist.append( ("Set-Cookie", "%s=%s; Max-Age=99999; Path=/; Secure; HttpOnly%s" % (self.sslCookieName, self.sslKey, sameSite))) except Exception as e: raise # FIXME logging.exception(e)
def normalizeKey(key: Union[None, 'db.KeyClass']) -> Union[None, 'db.KeyClass']: """ Normalizes a datastore key (replacing _application with the current one) :param key: Key to be normalized. :return: Normalized key in string representation. """ if key is None: return None if key.parent: parent = normalizeKey(key.parent) else: parent = None return db.Key(key.kind, key.id_or_name, parent=parent)
def load(self, req): """ Initializes the Session. If the client supplied a valid Cookie, the session is read from the memcache/datastore, otherwise a new, empty session will be initialized. """ self.changed = False self.isInitial = False self.httpKey = None self.sslKey = None self.staticSecurityKey = None self.securityKey = None self.session = {} if self.plainCookieName in req.request.cookies: cookie = str(req.request.cookies[self.plainCookieName]) try: data = db.Get(db.Key(self.kindName, cookie)) except: raise # FIXME return False if data: # Loaded successfully from Memcache if data["lastseen"] < time() - conf["viur.session.lifeTime"]: # This session is too old self.reset() return False self.session = pickle.loads(base64.b64decode(data["data"])) self.sslKey = data["sslkey"] self.staticSecurityKey = data["staticSecurityKey"] self.securityKey = data["securityKey"] self.httpKey = cookie if data["lastseen"] < time( ) - 5 * 60: # Refresh every 5 Minutes self.changed = True else: # We could not load from firebase; create a new one self.reset() if req.isSSLConnection and self.sslKey and not req.request.cookies.get( self.sslCookieName) == self.sslKey: logging.critical( "Possible session hijack attempt! Session dropped.") self.reset() return False return True else: self.reset()
def startProcessing(self, step, orderID): def setTokenTxn(key, token): order = db.Get(key) if not order: return order["paypal_token"] = urllib.unquote(token) db.Put(order) paypal = PayPal.PayPalHandler() key = db.Key(orderID) order = db.Get(key) if not order: return token = paypal.SetExpressCheckout("%.2f" % order["price"]) db.RunInTransaction(setTokenTxn, key, token) raise (errors.Redirect(paypal.getPayURL(token)))
def shortKey(render, val): """ Jinja2 filter: Make a shortkey from an entity-key. :param val: Entity-key as string. :type val: str :returns: Shortkey on success, None on error. :rtype: str """ try: k = db.Key(encoded=str(val)) return k.id_or_name() except: return None
def calculateOrderSum(self, step, orderKey, *args, **kwargs): """ Calculates the final price for this order. This function *must* be called before any attempt is made to start a payment process. :param step: Current step within the ordering process :type step: int :param orderKey: order to calculate the price for :type orderKey: str """ price = sum([x[3] for x in self.getBillItems(orderKey)]) orderObj = db.Get(db.Key(str(orderKey))) orderObj["price"] = price db.Put(orderObj)
def edit(self, *args, **kwargs): """ Modify the existing entry, and render the entry, eventually with error notes on incorrect data. The entry is fetched by its entity key, which either is provided via *kwargs["key"]*, or as the first parameter in *args*. The function performs several access control checks on the singleton's entity before it is modified. .. seealso:: :func:`editSkel`, :func:`onEdited`, :func:`canEdit` :returns: The rendered, edited object of the entry, eventually with error hints. :raises: :exc:`server.errors.Unauthorized`, if the current user does not have the required permissions. :raises: :exc:`server.errors.PreconditionFailed`, if the *skey* could not be verified. """ if "skey" in kwargs: skey = kwargs["skey"] else: skey = "" skel = self.editSkel() if not self.canEdit(): raise errors.Unauthorized() key = db.Key(self.editSkel().kindName, self.getKey()) if not skel.fromDB( key): # Its not there yet; we need to set the key again skel["key"] = key if (len(kwargs) == 0 # no data supplied or skey == "" # no skey provided or not skel.fromClient( kwargs) # failure on reading into the bones or ("bounce" in kwargs and kwargs["bounce"] == "1")): # review before changing return self.render.edit(skel) if not securitykey.validate(skey, useSessionKey=True): raise errors.PreconditionFailed() skel.toDB() self.onEdited(skel) return self.render.editSuccess(skel)
def filterHook(self, name, query, param, value): # FIXME """ Hook installed by buildDbFilter. This rewrites all filters added to the query after buildDbFilter has been run to match the layout of our viur-relations index. Also performs sanity checks wherever this query is possible at all. """ if param.startswith("src.") or param.startswith("dest.") or param.startswith("viur_"): # This filter is already valid in our relation return (param, value) if param.startswith("%s." % name): # We add a constrain filtering by properties of the referenced entity refKey = param.replace("%s." % name, "") if " " in refKey: # Strip >, < or = params refKey = refKey[:refKey.find(" ")] if refKey not in self.refKeys: logging.warning("Invalid filtering! %s is not in refKeys of RelationalBone %s!" % (refKey, name)) raise RuntimeError() if self.multiple: return (param.replace("%s." % name, "dest."), value) else: return (param, value) else: # We filter by a property of this entity if not self.multiple: # Not relational, not multiple - nothing to do here return (param, value) # Prepend "src." srcKey = param if " " in srcKey: srcKey = srcKey[: srcKey.find(" ")] # Cut <, >, and = if srcKey == db.KEY_SPECIAL_PROPERTY: # Rewrite key= filter as its meaning has changed if isinstance(value, list) or isinstance(value, tuple): logging.warning( "Invalid filtering! Doing an relational Query on %s with multiple key= filters is unsupported!" % ( name)) raise RuntimeError() if not isinstance(value, db.Key): value = db.Key(value) query.ancestor(value) return (None) if srcKey not in self.parentKeys: logging.warning("Invalid filtering! %s is not in parentKeys of RelationalBone %s!" % (srcKey, name)) raise RuntimeError() return ("src.%s" % param, value)
def save(self, req): """ Writes the session to the datastore. Does nothing, if the session hasn't been changed in the current request. """ try: if self.changed or self.isInitial: if not (req.isSSLConnection or req.isDevServer ): # We will not issue sessions over http anymore return False # Get the current user id try: # Check for our custom user-api userid = conf["viur.mainApp"].user.getCurrentUser()["key"] except: userid = None try: dbSession = db.Entity(db.Key(self.kindName, self.cookieKey)) dbSession["data"] = db.fixUnindexableProperties( self.session) dbSession["staticSecurityKey"] = self.staticSecurityKey dbSession["securityKey"] = self.securityKey dbSession["lastseen"] = time() # Store the userid inside the sessionobj, so we can kill specific sessions if needed dbSession["user"] = str(userid) or "guest" dbSession.exclude_from_indexes = ["data"] db.Put(dbSession) except Exception as e: logging.exception(e) raise # FIXME pass sameSite = "; SameSite=%s" % self.sameSite if self.sameSite else "" secure = "; Secure" if not req.isDevServer else "" maxAge = "; Max-Age=%s" % conf[ "viur.session.lifeTime"] if not self.sessionCookie else "" req.response.headerlist.append( ("Set-Cookie", "%s=%s; Path=/; HttpOnly%s%s%s" % (self.cookieName, self.cookieKey, sameSite, secure, maxAge))) except Exception as e: raise # FIXME logging.exception(e)
def create(duration: Union[None, int] = None, **kwargs) -> str: """ Creates a new onetime Securitykey or returns the current sessions csrf-token. The custom data (given as keyword arguments) that can be stored with the key if :param:duration is set must be serializable by the datastore. :param duration: Make this key valid for a fixed timeframe (and independent of the current session) :returns: The new onetime key """ if not duration: return currentSession.get().getSecurityKey() key = generateRandomString() duration = int(duration) dbObj = db.Entity(db.Key(securityKeyKindName, key)) for k, v in kwargs.items(): dbObj[k] = v dbObj["until"] = utcNow() + timedelta(seconds=duration) db.Put(dbObj) return key