Example #1
0
 def stage_ok(self) -> HttpResponse:
     """Callback called by stages upon successful completion.
     Persists updated plan and context to session."""
     self._logger.debug(
         "f(exec): Stage ok",
         stage_class=class_to_path(self.current_stage_view.__class__),
     )
     self.request.session.get(SESSION_KEY_HISTORY,
                              []).append(deepcopy(self.plan))
     self.plan.pop()
     self.request.session[SESSION_KEY_PLAN] = self.plan
     if self.plan.bindings:
         self._logger.debug(
             "f(exec): Continuing with next stage",
             remaining=len(self.plan.bindings),
         )
         kwargs = self.kwargs
         kwargs.update({"flow_slug": self.flow.slug})
         return redirect_with_qs("authentik_api:flow-executor",
                                 self.request.GET, **kwargs)
     # User passed all stages
     self._logger.debug(
         "f(exec): User passed all stages",
         context=cleanse_dict(self.plan.context),
     )
     return self._flow_done()
Example #2
0
def ldap_sync_all():
    """Sync all sources"""
    for source in LDAPSource.objects.filter(enabled=True):
        for sync_class in [
                UserLDAPSynchronizer,
                GroupLDAPSynchronizer,
                MembershipLDAPSynchronizer,
        ]:
            ldap_sync.delay(source.pk, class_to_path(sync_class))
Example #3
0
 def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
     """Solve the previously retrieved challenge and advanced to the next stage."""
     self._logger.debug(
         "f(exec): Passing POST",
         view_class=class_to_path(self.current_stage_view.__class__),
         stage=self.current_stage,
     )
     try:
         with Hub.current.start_span(
                 op="authentik.flow.executor.stage",
                 description=class_to_path(
                     self.current_stage_view.__class__),
         ) as span:
             span.set_data("Method", "POST")
             span.set_data("authentik Stage", self.current_stage_view)
             span.set_data("authentik Flow", self.flow.slug)
             stage_response = self.current_stage_view.post(
                 request, *args, **kwargs)
             return to_stage_response(request, stage_response)
     except Exception as exc:  # pylint: disable=broad-except
         return self.handle_exception(exc)
Example #4
0
 def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
     """Get the next pending challenge from the currently active flow."""
     self._logger.debug(
         "f(exec): Passing GET",
         view_class=class_to_path(self.current_stage_view.__class__),
         stage=self.current_stage,
     )
     try:
         with Hub.current.start_span(
                 op="authentik.flow.executor.stage",
                 description=class_to_path(
                     self.current_stage_view.__class__),
         ) as span:
             span.set_data("Method", "GET")
             span.set_data("authentik Stage", self.current_stage_view)
             span.set_data("authentik Flow", self.flow.slug)
             stage_response = self.current_stage_view.get(
                 request, *args, **kwargs)
             return to_stage_response(request, stage_response)
     except Exception as exc:  # pylint: disable=broad-except
         return self.handle_exception(exc)
Example #5
0
def post_save_update(sender, instance: Model, **_):
    """If an Outpost is saved, Ensure that token is created/updated

    If an OutpostModel, or a model that is somehow connected to an OutpostModel is saved,
    we send a message down the relevant OutpostModels WS connection to trigger an update"""
    if instance.__module__ == "django.db.migrations.recorder":
        return
    if instance.__module__ == "__fake__":
        return
    if not isinstance(instance, UPDATE_TRIGGERING_MODELS):
        return
    outpost_post_save.delay(class_to_path(instance.__class__), instance.pk)
Example #6
0
 def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
     """Solve the previously retrieved challenge and advanced to the next stage."""
     self._logger.debug(
         "f(exec): Passing POST",
         view_class=class_to_path(self.current_stage_view.__class__),
         stage=self.current_stage,
     )
     try:
         stage_response = self.current_stage_view.post(
             request, *args, **kwargs)
         return to_stage_response(request, stage_response)
     except Exception as exc:  # pylint: disable=broad-except
         self._logger.exception(exc)
         return to_stage_response(request, FlowErrorResponse(request, exc))
Example #7
0
 def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
     """Get the next pending challenge from the currently active flow."""
     self._logger.debug(
         "f(exec): Passing GET",
         view_class=class_to_path(self.current_stage_view.__class__),
         stage=self.current_stage,
     )
     try:
         stage_response = self.current_stage_view.get(
             request, *args, **kwargs)
         return to_stage_response(request, stage_response)
     except Exception as exc:  # pylint: disable=broad-except
         self._logger.exception(exc)
         return to_stage_response(request, FlowErrorResponse(request, exc))
Example #8
0
def sync_ldap_source_on_save(sender, instance: LDAPSource, **_):
    """Ensure that source is synced on save (if enabled)"""
    if not instance.enabled:
        return
    # Don't sync sources when they don't have any property mappings. This will only happen if:
    # - the user forgets to set them or
    # - the source is newly created, this is the first save event
    #   and the mappings are created with an m2m event
    if not instance.property_mappings.exists() or not instance.property_mappings_group.exists():
        return
    for sync_class in [
        UserLDAPSynchronizer,
        GroupLDAPSynchronizer,
        MembershipLDAPSynchronizer,
    ]:
        ldap_sync.delay(instance.pk, class_to_path(sync_class))
Example #9
0
 async def __call__(self, scope, receive, send):
     transaction: Optional[Transaction] = Hub.current.scope.transaction
     class_path = class_to_path(self.inner.consumer_class)
     if transaction:
         transaction.name = class_path
     return await self.inner(scope, receive, send)
Example #10
0
 def get(self, request: HttpRequest) -> HttpResponse:
     """Save data in the current flow to the currently pending user. If no user is pending,
     a new user is created."""
     if PLAN_CONTEXT_PROMPT not in self.executor.plan.context:
         message = _("No Pending data.")
         messages.error(request, message)
         LOGGER.debug(message)
         return self.executor.stage_invalid()
     data = self.executor.plan.context[PLAN_CONTEXT_PROMPT]
     user_created = False
     if PLAN_CONTEXT_PENDING_USER not in self.executor.plan.context:
         self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] = User()
         self.executor.plan.context[
             PLAN_CONTEXT_AUTHENTICATION_BACKEND
         ] = class_to_path(ModelBackend)
         LOGGER.debug(
             "Created new user",
             flow_slug=self.executor.flow.slug,
         )
         user_created = True
     user: User = self.executor.plan.context[PLAN_CONTEXT_PENDING_USER]
     # Before we change anything, check if the user is the same as in the request
     # and we're updating a password. In that case we need to update the session hash
     # Also check that we're not currently impersonating, so we don't update the session
     should_update_seesion = False
     if (
         any("password" in x for x in data.keys())
         and self.request.user.pk == user.pk
         and SESSION_IMPERSONATE_USER not in self.request.session
     ):
         should_update_seesion = True
     for key, value in data.items():
         setter_name = f"set_{key}"
         # Check if user has a setter for this key, like set_password
         if hasattr(user, setter_name):
             setter = getattr(user, setter_name)
             if callable(setter):
                 setter(value)
         # User has this key already
         elif hasattr(user, key):
             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("attribute_"):
                 LOGGER.debug("discarding key", key=key)
                 continue
             user.attributes[key.replace("attribute_", "", 1)] = value
     # Extra check to prevent flows from saving a user with a blank username
     if user.username == "":
         LOGGER.warning("Aborting write to empty username", user=user)
         return self.executor.stage_invalid()
     # 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 USER_ATTRIBUTE_SOURCES not in user.attributes or not isinstance(
             user.attributes.get(USER_ATTRIBUTE_SOURCES), list
         ):
             user.attributes[USER_ATTRIBUTE_SOURCES] = []
         connection: UserSourceConnection = self.executor.plan.context[
             PLAN_CONTEXT_SOURCES_CONNECTION
         ]
         user.attributes[USER_ATTRIBUTE_SOURCES].append(connection.source.name)
     user.save()
     user_write.send(
         sender=self, request=request, user=user, data=data, created=user_created
     )
     # Check if the password has been updated, and update the session auth hash
     if should_update_seesion:
         update_session_auth_hash(self.request, user)
         LOGGER.debug("Updated session hash", user=user)
     LOGGER.debug(
         "Updated existing user",
         user=user,
         flow_slug=self.executor.flow.slug,
     )
     return self.executor.stage_ok()
Example #11
0
def m2m_changed_update(sender, instance: Model, action: str, **_):
    """Update outpost on m2m change, when providers are added or removed"""
    if action in ["post_add", "post_remove", "post_clear"]:
        outpost_post_save.delay(class_to_path(instance.__class__), instance.pk)