stages/user_write: improve error handling (#5136)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens L 2023-03-31 23:59:37 +02:00 committed by GitHub
parent 986d7bf714
commit 5947c7b97e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 20 additions and 9 deletions

View File

@ -12,6 +12,7 @@ from authentik.core.models import USER_ATTRIBUTE_SOURCES, User, UserSourceConnec
from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
from authentik.flows.stage import StageView from authentik.flows.stage import StageView
from authentik.flows.views.executor import FlowExecutorView
from authentik.stages.password import BACKEND_INBUILT from authentik.stages.password import BACKEND_INBUILT
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
@ -25,6 +26,12 @@ PLAN_CONTEXT_USER_PATH = "user_path"
class UserWriteStageView(StageView): class UserWriteStageView(StageView):
"""Finalise Enrollment flow by creating a user object.""" """Finalise Enrollment flow by creating a user object."""
def __init__(self, executor: FlowExecutorView, **kwargs):
super().__init__(executor, **kwargs)
self.disallowed_user_attributes = [
"groups",
]
@staticmethod @staticmethod
def write_attribute(user: User, key: str, value: Any): def write_attribute(user: User, key: str, value: Any):
"""Allow use of attributes.foo.bar when writing to a user, with full """Allow use of attributes.foo.bar when writing to a user, with full
@ -90,19 +97,23 @@ class UserWriteStageView(StageView):
setter = getattr(user, setter_name) setter = getattr(user, setter_name)
if callable(setter): if callable(setter):
setter(value) setter(value)
elif key in self.disallowed_user_attributes:
self.logger.info("discarding key", key=key)
continue
# For exact attributes match, update the dictionary in place # For exact attributes match, update the dictionary in place
elif key == "attributes": elif key == "attributes":
user.attributes.update(value) user.attributes.update(value)
# User has this key already # If using dot notation, use the correct helper to update the nested value
elif hasattr(user, key) and not key.startswith("attributes."): elif key.startswith("attributes.") or key.startswith("attributes_"):
setattr(user, key, value)
# Otherwise we just save it as custom attribute, but only if the value is prefixed with
# `attribute_`, to prevent accidentally saving values
else:
if not key.startswith("attributes.") and not key.startswith("attributes_"):
self.logger.warning("discarding key", key=key)
continue
UserWriteStageView.write_attribute(user, key, value) UserWriteStageView.write_attribute(user, key, value)
# User has this key already
elif hasattr(user, key):
setattr(user, key, value)
# If none of the cases above matched, we have an attribute that the user doesn't have,
# has no setter for, is not a nested attributes value and as such is invalid
else:
self.logger.info("discarding key", key=key)
continue
# Check if we're writing from a source, and save the source to the attributes # Check if we're writing from a source, and save the source to the attributes
if PLAN_CONTEXT_SOURCES_CONNECTION in self.executor.plan.context: if PLAN_CONTEXT_SOURCES_CONNECTION in self.executor.plan.context:
if USER_ATTRIBUTE_SOURCES not in user.attributes or not isinstance( if USER_ATTRIBUTE_SOURCES not in user.attributes or not isinstance(