Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
This commit is contained in:
Marc 'risson' Schmitt 2023-12-27 11:51:37 +01:00
parent afc968437d
commit 18d7395e7e
No known key found for this signature in database
GPG Key ID: 9C3FA22FABF1AA8D
4 changed files with 48 additions and 14 deletions

View File

@ -95,6 +95,9 @@ outposts:
discover: true
disable_embedded_outpost: false
expressions:
restricted: false
ldap:
task_timeout_hours: 2
page_size: 50

View File

@ -6,15 +6,18 @@ from textwrap import indent
from typing import Any, Iterable, Optional
from cachetools import TLRUCache, cached
from django.apps import apps
from django.core.exceptions import FieldError
from guardian.shortcuts import get_anonymous_user
from rest_framework.serializers import ValidationError
from RestrictedPython import compile_restricted, limited_builtins, safe_builtins, utility_builtins
from sentry_sdk.hub import Hub
from sentry_sdk.tracing import Span
from structlog.stdlib import get_logger
from authentik.core.models import User
from authentik.events.models import Event
from authentik.lib.config import CONFIG
from authentik.lib.utils.http import get_http_session
from authentik.policies.models import Policy, PolicyBinding
from authentik.policies.process import PolicyProcess
@ -55,6 +58,10 @@ class BaseEvaluator:
"resolve_dns": BaseEvaluator.expr_resolve_dns,
"reverse_dns": BaseEvaluator.expr_reverse_dns,
}
for app in apps.get_app_configs():
# Load models from each app
for model in app.get_models():
self._globals[model.__name__] = model
self._context = {}
@cached(cache=TLRUCache(maxsize=32, ttu=lambda key, value, now: now + 180))
@ -180,6 +187,18 @@ class BaseEvaluator:
full_expression += f"\nresult = handler({handler_signature})"
return full_expression
def compile(self, expression: str) -> Any:
"""Parse expression. Raises SyntaxError or ValueError if the syntax is incorrect."""
param_keys = self._context.keys()
compiler = (
compile_restricted if CONFIG.get_bool("epxressions.restricted", False) else compile
)
return compiler(
self.wrap_expression(expression, param_keys),
self._filename,
"exec",
)
def evaluate(self, expression_source: str) -> Any:
"""Parse and evaluate expression. If the syntax is incorrect, a SyntaxError is raised.
If any exception is raised during execution, it is raised.
@ -188,17 +207,18 @@ class BaseEvaluator:
span: Span
span.description = self._filename
span.set_data("expression", expression_source)
param_keys = self._context.keys()
try:
ast_obj = compile(
self.wrap_expression(expression_source, param_keys),
self._filename,
"exec",
)
ast_obj = self.compile(expression_source)
except (SyntaxError, ValueError) as exc:
self.handle_error(exc, expression_source)
raise exc
try:
if CONFIG.get_bool("expressions.restricted", False):
self._globals["__builtins__"] = {
**safe_builtins,
**limited_builtins,
**utility_builtins,
}
_locals = self._context
# Yes this is an exec, yes it is potentially bad. Since we limit what variables are
# available here, and these policies can only be edited by admins, this is a risk
@ -221,13 +241,8 @@ class BaseEvaluator:
def validate(self, expression: str) -> bool:
"""Validate expression's syntax, raise ValidationError if Syntax is invalid"""
param_keys = self._context.keys()
try:
compile(
self.wrap_expression(expression, param_keys),
self._filename,
"exec",
)
self.compile(expression)
return True
except (ValueError, SyntaxError) as exc:
raise ValidationError(f"Expression Syntax Error: {str(exc)}") from exc

19
poetry.lock generated
View File

@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand.
[[package]]
name = "aiohttp"
@ -3279,6 +3279,21 @@ requests = ">=2.0.0"
[package.extras]
rsa = ["oauthlib[signedtoken] (>=3.0.0)"]
[[package]]
name = "restrictedpython"
version = "7.0"
description = "RestrictedPython is a defined subset of the Python language which allows to provide a program input into a trusted environment."
optional = false
python-versions = ">=3.7, <3.13"
files = [
{file = "RestrictedPython-7.0-py3-none-any.whl", hash = "sha256:8bb40a822090bed9c7b814d69345b0796db70cc86715d141efc937862f37c6d2"},
{file = "RestrictedPython-7.0.tar.gz", hash = "sha256:53704afbbc350fdc8fb245441367be671c9f8380869201b2e8452e74fce3db14"},
]
[package.extras]
docs = ["Sphinx", "sphinx-rtd-theme"]
test = ["pytest", "pytest-mock"]
[[package]]
name = "rich"
version = "13.7.0"
@ -4437,4 +4452,4 @@ files = [
[metadata]
lock-version = "2.0"
python-versions = "~3.12"
content-hash = "d0fe6ae1be389f8a5ca5112aa90555e2ce0a4f336f07a1da9c43dd521e9d9340"
content-hash = "0782627c112f4cefa27fa066eb1e4c9b01882b416690f4e1348f4e61dfe02190"

View File

@ -157,6 +157,7 @@ pyjwt = "*"
python = "~3.12"
pyyaml = "*"
requests-oauthlib = "*"
restrictedpython = "*"
sentry-sdk = "*"
service_identity = "*"
structlog = "*"