104 lines
3.6 KiB
Python
104 lines
3.6 KiB
Python
from typing import Any
|
|
|
|
from django.http import HttpRequest
|
|
from structlog.stdlib import get_logger
|
|
|
|
from authentik.core.expression.exceptions import PropertyMappingExpressionException
|
|
from authentik.core.models import Group, PropertyMapping, Source, User
|
|
from authentik.events.models import Event, EventAction
|
|
from authentik.lib.merge import MERGE_LIST_UNIQUE
|
|
from authentik.lib.sync.mapper import PropertyMappingManager
|
|
from authentik.policies.utils import delete_none_values
|
|
|
|
LOGGER = get_logger()
|
|
|
|
|
|
class SourceMapper:
|
|
def __init__(self, source: Source):
|
|
self.source = source
|
|
|
|
def get_manager(
|
|
self, object_type: type[User | Group], context_keys: list[str]
|
|
) -> PropertyMappingManager:
|
|
"""Get property mapping manager for this source."""
|
|
|
|
qs = PropertyMapping.objects.none()
|
|
if object_type == User:
|
|
qs = self.source.user_property_mappings.all().select_subclasses()
|
|
elif object_type == Group:
|
|
qs = self.source.group_property_mappings.all().select_subclasses()
|
|
qs = qs.order_by("name")
|
|
return PropertyMappingManager(
|
|
qs,
|
|
self.source.property_mapping_type,
|
|
["source", "properties"] + context_keys,
|
|
)
|
|
|
|
def get_base_properties(
|
|
self, object_type: type[User | Group], **kwargs
|
|
) -> dict[str, Any | dict[str, Any]]:
|
|
"""Get base properties for a user or a group to build final properties upon."""
|
|
if object_type == User:
|
|
properties = self.source.get_base_user_properties(**kwargs)
|
|
properties.setdefault("path", self.source.get_user_path())
|
|
return properties
|
|
if object_type == Group:
|
|
return self.source.get_base_group_properties(**kwargs)
|
|
return {}
|
|
|
|
def build_object_properties(
|
|
self,
|
|
object_type: type[User | Group],
|
|
manager: "PropertyMappingManager | None" = None,
|
|
user: User | None = None,
|
|
request: HttpRequest | None = None,
|
|
**kwargs,
|
|
) -> dict[str, Any | dict[str, Any]]:
|
|
"""Build a user or group properties from the source configured property mappings."""
|
|
|
|
properties = self.get_base_properties(object_type, **kwargs)
|
|
if "attributes" not in properties:
|
|
properties["attributes"] = {}
|
|
|
|
if not manager:
|
|
manager = self.get_manager(object_type, list(kwargs.keys()))
|
|
evaluations = manager.iter_eval(
|
|
user=user,
|
|
request=request,
|
|
return_mapping=True,
|
|
source=self.source,
|
|
properties=properties,
|
|
**kwargs,
|
|
)
|
|
while True:
|
|
try:
|
|
value, mapping = next(evaluations)
|
|
except StopIteration:
|
|
break
|
|
except PropertyMappingExpressionException as exc:
|
|
Event.new(
|
|
EventAction.CONFIGURATION_ERROR,
|
|
message=f"Failed to evaluate property mapping: '{exc.mapping.name}'",
|
|
source=self,
|
|
mapping=exc.mapping,
|
|
).save()
|
|
LOGGER.warning(
|
|
"Mapping failed to evaluate",
|
|
exc=exc,
|
|
source=self,
|
|
mapping=exc.mapping,
|
|
)
|
|
raise exc
|
|
|
|
if not value or not isinstance(value, dict):
|
|
LOGGER.debug(
|
|
"Mapping evaluated to None or is not a dict. Skipping",
|
|
source=self,
|
|
mapping=mapping,
|
|
)
|
|
continue
|
|
|
|
MERGE_LIST_UNIQUE.merge(properties, value)
|
|
|
|
return delete_none_values(properties)
|