providers/saml: better handle PropertyMapping evaluation errors
This commit is contained in:
parent
aeca66a288
commit
813b2676de
5
passbook/core/exceptions.py
Normal file
5
passbook/core/exceptions.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
"""passbook core exceptions"""
|
||||||
|
|
||||||
|
|
||||||
|
class PropertyMappingExpressionException(Exception):
|
||||||
|
"""Error when a PropertyMapping Exception expression could not be parsed or evaluated."""
|
|
@ -5,6 +5,7 @@ from time import sleep
|
||||||
from typing import Optional, Any
|
from typing import Optional, Any
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
|
from jinja2.exceptions import TemplateSyntaxError, UndefinedError
|
||||||
from jinja2.nativetypes import NativeEnvironment
|
from jinja2.nativetypes import NativeEnvironment
|
||||||
from django.contrib.auth.models import AbstractUser
|
from django.contrib.auth.models import AbstractUser
|
||||||
from django.contrib.postgres.fields import JSONField
|
from django.contrib.postgres.fields import JSONField
|
||||||
|
@ -18,6 +19,7 @@ from guardian.mixins import GuardianUserMixin
|
||||||
from model_utils.managers import InheritanceManager
|
from model_utils.managers import InheritanceManager
|
||||||
from structlog import get_logger
|
from structlog import get_logger
|
||||||
|
|
||||||
|
from passbook.core.exceptions import PropertyMappingExpressionException
|
||||||
from passbook.core.signals import password_changed
|
from passbook.core.signals import password_changed
|
||||||
from passbook.lib.models import CreatedUpdatedModel, UUIDModel
|
from passbook.lib.models import CreatedUpdatedModel, UUIDModel
|
||||||
from passbook.policies.exceptions import PolicyException
|
from passbook.policies.exceptions import PolicyException
|
||||||
|
@ -303,8 +305,14 @@ class PropertyMapping(UUIDModel):
|
||||||
|
|
||||||
def evaluate(self, user: User, request: HttpRequest, **kwargs) -> Any:
|
def evaluate(self, user: User, request: HttpRequest, **kwargs) -> Any:
|
||||||
"""Evaluate `self.expression` using `**kwargs` as Context."""
|
"""Evaluate `self.expression` using `**kwargs` as Context."""
|
||||||
expression = NATIVE_ENVIRONMENT.from_string(self.expression)
|
try:
|
||||||
return expression.render(user=user, request=request, **kwargs)
|
expression = NATIVE_ENVIRONMENT.from_string(self.expression)
|
||||||
|
except TemplateSyntaxError as exc:
|
||||||
|
raise PropertyMappingExpressionException from exc
|
||||||
|
try:
|
||||||
|
return expression.render(user=user, request=request, **kwargs)
|
||||||
|
except UndefinedError as exc:
|
||||||
|
raise PropertyMappingExpressionException from exc
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"Property Mapping {self.name}"
|
return f"Property Mapping {self.name}"
|
||||||
|
|
|
@ -5,6 +5,7 @@ from defusedxml import ElementTree
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
from structlog import get_logger
|
from structlog import get_logger
|
||||||
|
|
||||||
|
from passbook.core.exceptions import PropertyMappingExpressionException
|
||||||
from passbook.providers.saml.exceptions import CannotHandleAssertion
|
from passbook.providers.saml.exceptions import CannotHandleAssertion
|
||||||
from passbook.providers.saml.utils import get_random_id
|
from passbook.providers.saml.utils import get_random_id
|
||||||
from passbook.providers.saml.utils.encoding import decode_base64_and_inflate, nice64
|
from passbook.providers.saml.utils.encoding import decode_base64_and_inflate, nice64
|
||||||
|
@ -97,7 +98,10 @@ class Processor:
|
||||||
from passbook.providers.saml.models import SAMLPropertyMapping
|
from passbook.providers.saml.models import SAMLPropertyMapping
|
||||||
|
|
||||||
for mapping in self._remote.property_mappings.all().select_subclasses():
|
for mapping in self._remote.property_mappings.all().select_subclasses():
|
||||||
if isinstance(mapping, SAMLPropertyMapping):
|
if not isinstance(mapping, SAMLPropertyMapping):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
mapping: SAMLPropertyMapping
|
||||||
value = mapping.evaluate(
|
value = mapping.evaluate(
|
||||||
user=self._http_request.user,
|
user=self._http_request.user,
|
||||||
request=self._http_request,
|
request=self._http_request,
|
||||||
|
@ -107,11 +111,16 @@ class Processor:
|
||||||
"Name": mapping.saml_name,
|
"Name": mapping.saml_name,
|
||||||
"FriendlyName": mapping.friendly_name,
|
"FriendlyName": mapping.friendly_name,
|
||||||
}
|
}
|
||||||
|
# Normal values and arrays need different dict keys as they are handeled
|
||||||
|
# differently in the template
|
||||||
if isinstance(value, list):
|
if isinstance(value, list):
|
||||||
mapping_payload["ValueArray"] = value
|
mapping_payload["ValueArray"] = value
|
||||||
else:
|
else:
|
||||||
mapping_payload["Value"] = value
|
mapping_payload["Value"] = value
|
||||||
attributes.append(mapping_payload)
|
attributes.append(mapping_payload)
|
||||||
|
except PropertyMappingExpressionException as exc:
|
||||||
|
self._logger.warning(exc)
|
||||||
|
continue
|
||||||
self._assertion_params["ATTRIBUTES"] = attributes
|
self._assertion_params["ATTRIBUTES"] = attributes
|
||||||
self._assertion_xml = get_assertion_xml(
|
self._assertion_xml = get_assertion_xml(
|
||||||
"saml/xml/assertions/generic.xml", self._assertion_params, signed=True
|
"saml/xml/assertions/generic.xml", self._assertion_params, signed=True
|
||||||
|
|
Reference in a new issue