core: fix missing argument name escaping for property mapping (#11231)
* escape property mapping args Signed-off-by: Jens Langhammer <jens@goauthentik.io> * improve display of error Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix error handling, missing dry_run argument Signed-off-by: Jens Langhammer <jens@goauthentik.io> * use different sanitisation Signed-off-by: Jens Langhammer <jens@goauthentik.io> * update docs Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@ -30,8 +30,10 @@ from authentik.core.api.utils import (
|
||||
PassiveSerializer,
|
||||
)
|
||||
from authentik.core.expression.evaluator import PropertyMappingEvaluator
|
||||
from authentik.core.expression.exceptions import PropertyMappingExpressionException
|
||||
from authentik.core.models import Group, PropertyMapping, User
|
||||
from authentik.events.utils import sanitize_item
|
||||
from authentik.lib.utils.errors import exception_to_string
|
||||
from authentik.policies.api.exec import PolicyTestSerializer
|
||||
from authentik.rbac.decorators import permission_required
|
||||
|
||||
@ -162,12 +164,15 @@ class PropertyMappingViewSet(
|
||||
|
||||
response_data = {"successful": True, "result": ""}
|
||||
try:
|
||||
result = mapping.evaluate(**context)
|
||||
result = mapping.evaluate(dry_run=True, **context)
|
||||
response_data["result"] = dumps(
|
||||
sanitize_item(result), indent=(4 if format_result else None)
|
||||
)
|
||||
except PropertyMappingExpressionException as exc:
|
||||
response_data["result"] = exception_to_string(exc.exc)
|
||||
response_data["successful"] = False
|
||||
except Exception as exc:
|
||||
response_data["result"] = str(exc)
|
||||
response_data["result"] = exception_to_string(exc)
|
||||
response_data["successful"] = False
|
||||
response = PropertyMappingTestResultSerializer(response_data)
|
||||
return Response(response.data)
|
||||
|
@ -901,7 +901,7 @@ class PropertyMapping(SerializerModel, ManagedModel):
|
||||
except ControlFlowException as exc:
|
||||
raise exc
|
||||
except Exception as exc:
|
||||
raise PropertyMappingExpressionException(self, exc) from exc
|
||||
raise PropertyMappingExpressionException(exc, self) from exc
|
||||
|
||||
def __str__(self):
|
||||
return f"Property Mapping {self.name}"
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
import re
|
||||
import socket
|
||||
from collections.abc import Iterable
|
||||
from ipaddress import ip_address, ip_network
|
||||
from textwrap import indent
|
||||
from types import CodeType
|
||||
@ -28,6 +27,12 @@ from authentik.stages.authenticator import devices_for_user
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
ARG_SANITIZE = re.compile(r"[:.-]")
|
||||
|
||||
|
||||
def sanitize_arg(arg_name: str) -> str:
|
||||
return re.sub(ARG_SANITIZE, "_", arg_name)
|
||||
|
||||
|
||||
class BaseEvaluator:
|
||||
"""Validate and evaluate python-based expressions"""
|
||||
@ -177,9 +182,9 @@ class BaseEvaluator:
|
||||
proc = PolicyProcess(PolicyBinding(policy=policy), request=req, connection=None)
|
||||
return proc.profiling_wrapper()
|
||||
|
||||
def wrap_expression(self, expression: str, params: Iterable[str]) -> str:
|
||||
def wrap_expression(self, expression: str) -> str:
|
||||
"""Wrap expression in a function, call it, and save the result as `result`"""
|
||||
handler_signature = ",".join(params)
|
||||
handler_signature = ",".join(sanitize_arg(x) for x in self._context.keys())
|
||||
full_expression = ""
|
||||
full_expression += f"def handler({handler_signature}):\n"
|
||||
full_expression += indent(expression, " ")
|
||||
@ -188,8 +193,8 @@ class BaseEvaluator:
|
||||
|
||||
def compile(self, expression: str) -> CodeType:
|
||||
"""Parse expression. Raises SyntaxError or ValueError if the syntax is incorrect."""
|
||||
param_keys = self._context.keys()
|
||||
return compile(self.wrap_expression(expression, param_keys), self._filename, "exec")
|
||||
expression = self.wrap_expression(expression)
|
||||
return compile(expression, self._filename, "exec")
|
||||
|
||||
def evaluate(self, expression_source: str) -> Any:
|
||||
"""Parse and evaluate expression. If the syntax is incorrect, a SyntaxError is raised.
|
||||
@ -205,7 +210,7 @@ class BaseEvaluator:
|
||||
self.handle_error(exc, expression_source)
|
||||
raise exc
|
||||
try:
|
||||
_locals = self._context
|
||||
_locals = {sanitize_arg(x): y for x, y in self._context.items()}
|
||||
# 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
|
||||
# we're willing to take.
|
||||
|
@ -61,7 +61,9 @@ export class PolicyTestForm extends Form<PropertyMappingTestRequest> {
|
||||
</ak-codemirror>`
|
||||
: html` <div class="pf-c-form__group-label">
|
||||
<div class="c-form__horizontal-group">
|
||||
<span class="pf-c-form__label-text">${this.result?.result}</span>
|
||||
<span class="pf-c-form__label-text">
|
||||
<pre>${this.result?.result}</pre>
|
||||
</span>
|
||||
</div>
|
||||
</div>`}
|
||||
</ak-form-element-horizontal>`;
|
||||
|
@ -34,6 +34,43 @@ See the [overview](../property-mappings/index.md) for information on how propert
|
||||
|
||||
### Expression data
|
||||
|
||||
The following variables are available to SCIM source property mappings:
|
||||
Each top level SCIM attribute is available as a variable in the expression. For example given an SCIM request with the payload of
|
||||
|
||||
- `data`: A Python dictionary containing data from the SCIM source.
|
||||
```json
|
||||
{
|
||||
"schemas": [
|
||||
"urn:scim:schemas:core:2.0",
|
||||
"urn:scim:schemas:extension:enterprise:2.0"
|
||||
],
|
||||
"userName": "foo.bar",
|
||||
"name": {
|
||||
"familyName": "bar",
|
||||
"givenName": "foo",
|
||||
"formatted": "foo.bar"
|
||||
},
|
||||
"emails": [
|
||||
{
|
||||
"value": "foo.bar@authentik.company",
|
||||
"type": "work",
|
||||
"primary": true
|
||||
}
|
||||
],
|
||||
"title": "",
|
||||
"urn:scim:schemas:extension:enterprise:2.0": {
|
||||
"department": ""
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The following variables are available in the expression:
|
||||
|
||||
- `schemas` as a list of strings
|
||||
- `userName` as a string
|
||||
- `name` as a dictionary
|
||||
- `emails` as a dictionary
|
||||
- `title` as a string
|
||||
- `urn_scim_schemas_extension_enterprise_2_0` as a dictionary
|
||||
|
||||
:::info
|
||||
Top-level keys which include symbols not allowed in python syntax are converted to `_`.
|
||||
:::
|
||||
|
Reference in New Issue
Block a user