Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Replace router/request flags by flags-set #902

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/viur/core/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ def parse_value_by_annotation(annotation: type, name: str, value: str | list | t
logging.debug(f"calling {self._func=} with cleaned {args=}, {kwargs=}")

# evaluate skey guard setting?
if self.skey and not current.request.get().skey_checked: # skey guardiance is only required once per request
if self.skey and "skey-checked" not in current.request.get().flags: # skey guardiance required once per request
if trace:
logging.debug(f"@skey {self.skey=}")

Expand Down Expand Up @@ -242,7 +242,7 @@ def parse_value_by_annotation(annotation: type, name: str, value: str | list | t

from viur.core import securitykey
payload = securitykey.validate(security_key, **self.skey["extra_kwargs"])
current.request.get().skey_checked = True
current.request.get().flags.add("skey-checked")

if not payload or (self.skey["validate"] and not self.skey["validate"](payload)):
raise errors.PreconditionFailed(
Expand Down
2 changes: 1 addition & 1 deletion src/viur/core/modules/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ def pwrecover(self, recovery_key: str | None = None, skey: str | None = None, *a
# This is the first step, where we ask for the username of the account we'll going to reset the password on
skel = self.LostPasswordStep1Skel()

if not current_request.isPostRequest or not skel.fromClient(kwargs):
if "method-post" not in current_request.flags or not skel.fromClient(kwargs):
return self._user_module.render.edit(skel, tpl=self.passwordRecoveryStep1Template)

# validate security key
Expand Down
13 changes: 8 additions & 5 deletions src/viur/core/render/html/env/viur.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ def execRequest(render: Render, path: str, *args, **kwargs) -> Any:
tmp_params = request.kwargs.copy()
request.kwargs = {"__args": args, "__outer": tmp_params}
request.kwargs.update(kwargs)
lastRequestState = request.internalRequest
request.internalRequest = True
lastRequestState = "exec-request" in request.flags
request.flags.add("exec-request")
last_template_style = request.template_style
request.template_style = template_style
caller = conf["viur.mainApp"]
Expand All @@ -90,13 +90,15 @@ def execRequest(render: Render, path: str, *args, **kwargs) -> Any:
caller = getattr(caller, "index")
else:
request.kwargs = tmp_params # Reset RequestParams
request.internalRequest = lastRequestState
if not lastRequestState:
request.flags.remove("exec-request")
request.template_style = last_template_style
return u"Path not found %s (failed Part was %s)" % (path, currpath)

if not (isinstance(caller, Method) and caller.exposed is not None):
request.kwargs = tmp_params # Reset RequestParams
request.internalRequest = lastRequestState
if not lastRequestState:
request.flags.remove("exec-request")
request.template_style = last_template_style
return u"%s not callable or not exposed" % str(caller)
try:
Expand All @@ -106,7 +108,8 @@ def execRequest(render: Render, path: str, *args, **kwargs) -> Any:
logging.exception(e)
raise
request.kwargs = tmp_params
request.internalRequest = lastRequestState
if not lastRequestState:
request.flags.remove("exec-request")
request.template_style = last_template_style
if cachetime:
pass
Expand Down
2 changes: 1 addition & 1 deletion src/viur/core/render/vi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def canAccess(*args, **kwargs) -> bool:
def index(*args, **kwargs):
if not conf["viur.instance.project_base_path"].joinpath("vi", "main.html").exists():
raise errors.NotFound()
if conf["viur.instance.is_dev_server"] or current.request.get().isSSLConnection:
if conf["viur.instance.is_dev_server"] or "ssl" in current.request.get().flags:
raise errors.Redirect("/vi/s/main.html")
else:
appVersion = current.request.get().request.host
Expand Down
56 changes: 32 additions & 24 deletions src/viur/core/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,21 @@ class FetchMetaDataValidator(RequestValidator):
@staticmethod
def validate(request: 'BrowseHandler') -> typing.Optional[typing.Tuple[int, str, str]]:
headers = request.request.headers

if not headers.get("sec-fetch-site"): # These headers are not send by all browsers
return None

if headers.get('sec-fetch-site') in {"same-origin", "none"}: # A Request from our site
return None

if os.environ['GAE_ENV'] == "localdev" and headers.get('sec-fetch-site') == "same-site":
# We are accepting a request with same-site only in local dev mode
return None
if headers.get('sec-fetch-mode') == 'navigate' and not request.isPostRequest \

if headers.get("sec-fetch-mode") == "navigate" and "method-post" not in request.flags \
and headers.get('sec-fetch-dest') not in {'object', 'embed'}: # Incoming navigation GET request
return None

return 403, "Forbidden", "Request rejected due to fetch metadata"


Expand Down Expand Up @@ -110,23 +115,25 @@ def __init__(self, environ: dict):
self.maxLogLevel = logging.DEBUG
self._traceID = \
self.request.headers.get("X-Cloud-Trace-Context", "").split("/")[0] or utils.generateRandomString()
self.is_deferred = False
self.path_list = ()
self.flags = {"method-" + self.request.method.lower()}
if self.request.host_url.lower().startswith("https://"):
self.flags.add("ssl")

# TODO: Create compatiblity layer and deprecation warnings for:
# self.is_deferred = False # deferred
# self.skey_checked = False # skey-checked
# self.internalRequest = False # exec-request
# self.disableCache = False # nocache
# self.isPostRequest # method-post
# self.isSSLConnection # ssl

self.skey_checked = False # indicates whether @skey-decorator-check has already performed within a request
self.internalRequest = False
self.disableCache = False # Shall this request bypass the caches?
self.pendingTasks = []
self.args = ()
self.kwargs = {}
self.context = {}
self.template_style: str | None = None

# Check if it's a HTTP-Method we support
self.method = self.request.method.lower()
self.isPostRequest = self.method == "post"
self.isSSLConnection = self.request.host_url.lower().startswith("https://") # We have an encrypted channel

db.currentDbAccessLog.set(set())

# Set context variables
Expand Down Expand Up @@ -207,10 +214,11 @@ def _process(self):
return

if self.request.headers.get("X-AppEngine-TaskName", None) is not None: # Check if we run in the appengine
if self.request.environ.get("HTTP_X_APPENGINE_USER_IP") in _appengineServiceIPs:
self.is_deferred = True
elif os.getenv("TASKS_EMULATOR") is not None:
self.is_deferred = True
if (
self.request.environ.get("HTTP_X_APPENGINE_USER_IP") in _appengineServiceIPs
or os.getenv("TASKS_EMULATOR") is not None
):
self.flags.add("deferred")

current.language.set(conf["viur.defaultLanguage"])

Expand All @@ -229,7 +237,7 @@ def _process(self):
if conf["viur.security.contentSecurityPolicy"] and conf["viur.security.contentSecurityPolicy"]["_headerCache"]:
for k, v in conf["viur.security.contentSecurityPolicy"]["_headerCache"].items():
self.response.headers[k] = v
if self.isSSLConnection: # Check for HTST and PKP headers only if we have a secure channel.
if "ssl" in self.flags: # Check for HTST and PKP headers only if we have a secure channel.
if conf["viur.security.strictTransportSecurity"]:
self.response.headers["Strict-Transport-Security"] = conf["viur.security.strictTransportSecurity"]
# Check for X-Security-Headers we shall emit
Expand Down Expand Up @@ -261,7 +269,7 @@ def _process(self):
self.response.headers["Cross-Origin-Resource-Policy"] = conf["viur.security.enableCORP"]

# Ensure that TLS is used if required
if conf["viur.forceSSL"] and not self.isSSLConnection and not conf["viur.instance.is_dev_server"]:
if conf["viur.forceSSL"] and "ssl" not in self.flags and not conf["viur.instance.is_dev_server"]:
isWhitelisted = False
reqPath = self.request.path
for testUrl in conf["viur.noSSLCheckUrls"]:
Expand Down Expand Up @@ -410,7 +418,7 @@ def _process(self):
)

if conf["viur.instance.is_dev_server"]:
self.is_deferred = True
self.flags.add("deferred")

while self.pendingTasks:
task = self.pendingTasks.pop()
Expand Down Expand Up @@ -508,26 +516,26 @@ def _route(self, path: str) -> None:
raise errors.MethodNotAllowed()

# Check for internal exposed
if caller.exposed is False and not self.internalRequest:
if caller.exposed is False and "exec-request" not in self.flags:
raise errors.NotFound()

# Check for @force_ssl flag
if not self.internalRequest \
if "exec-request" not in self.flags \
and caller.ssl \
and not self.request.host_url.lower().startswith("https://") \
and not conf["viur.instance.is_dev_server"]:
raise errors.PreconditionFailed("You must use SSL to access this resource!")

# Check for @force_post flag
if not self.isPostRequest and caller.methods == ("POST", ):
if "method-post" not in self.flags and caller.methods == ("POST", ):
raise errors.MethodNotAllowed("You must use POST to access this resource!")

# Check if this request should bypass the caches
if self.request.headers.get("X-Viur-Disable-Cache"):
if self.request.headers.get("X-Viur-nocache"):
# No cache requested, check if the current user is allowed to do so
if (user := current.user.get()) and "root" in user["access"]:
logging.debug("Caching disabled by X-Viur-Disable-Cache header")
self.disableCache = True
logging.debug("Caching disabled by X-Viur-nocache header")
self.flags.add("nocache")

# Destill context as self.context, if available
if context := {k: v for k, v in self.kwargs.items() if k.startswith("@")}:
Expand All @@ -538,7 +546,7 @@ def _route(self, path: str) -> None:
else:
kwargs = self.kwargs

if ((self.internalRequest and conf["viur.debug.traceInternalCallRouting"])
if (("exec-request" in self.flags and conf["viur.debug.traceInternalCallRouting"])
or conf["viur.debug.traceExternalCallRouting"]):
logging.debug(
f"Calling {caller._func!r} with args={self.args!r}, {kwargs=} within context={self.context!r}"
Expand Down
2 changes: 1 addition & 1 deletion src/viur/core/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def save(self, req):
return

# We will not issue sessions over http anymore
if not (req.isSSLConnection or conf["viur.instance.is_dev_server"]):
if not ("ssl" in req.flags or conf["viur.instance.is_dev_server"]):
return

# Get the current user's key
Expand Down