wip
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
This commit is contained in:
parent
afc968437d
commit
18d7395e7e
|
@ -95,6 +95,9 @@ outposts:
|
||||||
discover: true
|
discover: true
|
||||||
disable_embedded_outpost: false
|
disable_embedded_outpost: false
|
||||||
|
|
||||||
|
expressions:
|
||||||
|
restricted: false
|
||||||
|
|
||||||
ldap:
|
ldap:
|
||||||
task_timeout_hours: 2
|
task_timeout_hours: 2
|
||||||
page_size: 50
|
page_size: 50
|
||||||
|
|
|
@ -6,15 +6,18 @@ from textwrap import indent
|
||||||
from typing import Any, Iterable, Optional
|
from typing import Any, Iterable, Optional
|
||||||
|
|
||||||
from cachetools import TLRUCache, cached
|
from cachetools import TLRUCache, cached
|
||||||
|
from django.apps import apps
|
||||||
from django.core.exceptions import FieldError
|
from django.core.exceptions import FieldError
|
||||||
from guardian.shortcuts import get_anonymous_user
|
from guardian.shortcuts import get_anonymous_user
|
||||||
from rest_framework.serializers import ValidationError
|
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.hub import Hub
|
||||||
from sentry_sdk.tracing import Span
|
from sentry_sdk.tracing import Span
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.events.models import Event
|
from authentik.events.models import Event
|
||||||
|
from authentik.lib.config import CONFIG
|
||||||
from authentik.lib.utils.http import get_http_session
|
from authentik.lib.utils.http import get_http_session
|
||||||
from authentik.policies.models import Policy, PolicyBinding
|
from authentik.policies.models import Policy, PolicyBinding
|
||||||
from authentik.policies.process import PolicyProcess
|
from authentik.policies.process import PolicyProcess
|
||||||
|
@ -55,6 +58,10 @@ class BaseEvaluator:
|
||||||
"resolve_dns": BaseEvaluator.expr_resolve_dns,
|
"resolve_dns": BaseEvaluator.expr_resolve_dns,
|
||||||
"reverse_dns": BaseEvaluator.expr_reverse_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 = {}
|
self._context = {}
|
||||||
|
|
||||||
@cached(cache=TLRUCache(maxsize=32, ttu=lambda key, value, now: now + 180))
|
@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})"
|
full_expression += f"\nresult = handler({handler_signature})"
|
||||||
return full_expression
|
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:
|
def evaluate(self, expression_source: str) -> Any:
|
||||||
"""Parse and evaluate expression. If the syntax is incorrect, a SyntaxError is raised.
|
"""Parse and evaluate expression. If the syntax is incorrect, a SyntaxError is raised.
|
||||||
If any exception is raised during execution, it is raised.
|
If any exception is raised during execution, it is raised.
|
||||||
|
@ -188,17 +207,18 @@ class BaseEvaluator:
|
||||||
span: Span
|
span: Span
|
||||||
span.description = self._filename
|
span.description = self._filename
|
||||||
span.set_data("expression", expression_source)
|
span.set_data("expression", expression_source)
|
||||||
param_keys = self._context.keys()
|
|
||||||
try:
|
try:
|
||||||
ast_obj = compile(
|
ast_obj = self.compile(expression_source)
|
||||||
self.wrap_expression(expression_source, param_keys),
|
|
||||||
self._filename,
|
|
||||||
"exec",
|
|
||||||
)
|
|
||||||
except (SyntaxError, ValueError) as exc:
|
except (SyntaxError, ValueError) as exc:
|
||||||
self.handle_error(exc, expression_source)
|
self.handle_error(exc, expression_source)
|
||||||
raise exc
|
raise exc
|
||||||
try:
|
try:
|
||||||
|
if CONFIG.get_bool("expressions.restricted", False):
|
||||||
|
self._globals["__builtins__"] = {
|
||||||
|
**safe_builtins,
|
||||||
|
**limited_builtins,
|
||||||
|
**utility_builtins,
|
||||||
|
}
|
||||||
_locals = self._context
|
_locals = self._context
|
||||||
# Yes this is an exec, yes it is potentially bad. Since we limit what variables are
|
# 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
|
# 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:
|
def validate(self, expression: str) -> bool:
|
||||||
"""Validate expression's syntax, raise ValidationError if Syntax is invalid"""
|
"""Validate expression's syntax, raise ValidationError if Syntax is invalid"""
|
||||||
param_keys = self._context.keys()
|
|
||||||
try:
|
try:
|
||||||
compile(
|
self.compile(expression)
|
||||||
self.wrap_expression(expression, param_keys),
|
|
||||||
self._filename,
|
|
||||||
"exec",
|
|
||||||
)
|
|
||||||
return True
|
return True
|
||||||
except (ValueError, SyntaxError) as exc:
|
except (ValueError, SyntaxError) as exc:
|
||||||
raise ValidationError(f"Expression Syntax Error: {str(exc)}") from exc
|
raise ValidationError(f"Expression Syntax Error: {str(exc)}") from exc
|
||||||
|
|
19
poetry.lock
generated
19
poetry.lock
generated
|
@ -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]]
|
[[package]]
|
||||||
name = "aiohttp"
|
name = "aiohttp"
|
||||||
|
@ -3279,6 +3279,21 @@ requests = ">=2.0.0"
|
||||||
[package.extras]
|
[package.extras]
|
||||||
rsa = ["oauthlib[signedtoken] (>=3.0.0)"]
|
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]]
|
[[package]]
|
||||||
name = "rich"
|
name = "rich"
|
||||||
version = "13.7.0"
|
version = "13.7.0"
|
||||||
|
@ -4437,4 +4452,4 @@ files = [
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "~3.12"
|
python-versions = "~3.12"
|
||||||
content-hash = "d0fe6ae1be389f8a5ca5112aa90555e2ce0a4f336f07a1da9c43dd521e9d9340"
|
content-hash = "0782627c112f4cefa27fa066eb1e4c9b01882b416690f4e1348f4e61dfe02190"
|
||||||
|
|
|
@ -157,6 +157,7 @@ pyjwt = "*"
|
||||||
python = "~3.12"
|
python = "~3.12"
|
||||||
pyyaml = "*"
|
pyyaml = "*"
|
||||||
requests-oauthlib = "*"
|
requests-oauthlib = "*"
|
||||||
|
restrictedpython = "*"
|
||||||
sentry-sdk = "*"
|
sentry-sdk = "*"
|
||||||
service_identity = "*"
|
service_identity = "*"
|
||||||
structlog = "*"
|
structlog = "*"
|
||||||
|
|
Reference in a new issue