Exemple #1
0
    def process(
        self,
        plan: "FlowPlan",
        binding: FlowStageBinding,
        http_request: HttpRequest,
    ) -> Optional[FlowStageBinding]:
        """Re-evaluate policies bound to stage, and if they fail, remove from plan"""
        from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER

        LOGGER.debug(
            "f(plan_inst)[re-eval marker]: running re-evaluation",
            binding=binding,
            policy_binding=self.binding,
        )
        engine = PolicyEngine(
            self.binding, plan.context.get(PLAN_CONTEXT_PENDING_USER, http_request.user)
        )
        engine.use_cache = False
        engine.request.set_http_request(http_request)
        engine.request.context = plan.context
        engine.build()
        result = engine.result
        if result.passing:
            return binding
        LOGGER.warning(
            "f(plan_inst)[re-eval marker]: binding failed re-evaluation",
            binding=binding,
            messages=result.messages,
        )
        return None
Exemple #2
0
def event_trigger_handler(event_uuid: str, trigger_name: str):
    """Check if policies attached to NotificationRule match event"""
    events = Event.objects.filter(event_uuid=event_uuid)
    if not events.exists():
        LOGGER.warning("event doesn't exist yet or anymore", event_uuid=event_uuid)
        return
    event: Event = events.first()
    triggers: NotificationRule = NotificationRule.objects.filter(name=trigger_name)
    if not triggers.exists():
        return
    trigger = triggers.first()

    if "policy_uuid" in event.context:
        policy_uuid = event.context["policy_uuid"]
        if PolicyBinding.objects.filter(
            target__in=NotificationRule.objects.all().values_list("pbm_uuid", flat=True),
            policy=policy_uuid,
        ).exists():
            # If policy that caused this event to be created is attached
            # to *any* NotificationRule, we return early.
            # This is the most effective way to prevent infinite loops.
            LOGGER.debug("e(trigger): attempting to prevent infinite loop", trigger=trigger)
            return

    if not trigger.group:
        LOGGER.debug("e(trigger): trigger has no group", trigger=trigger)
        return

    LOGGER.debug("e(trigger): checking if trigger applies", trigger=trigger)
    try:
        user = User.objects.filter(pk=event.user.get("pk")).first() or get_anonymous_user()
    except User.DoesNotExist:
        LOGGER.warning("e(trigger): failed to get user", trigger=trigger)
        return
    policy_engine = PolicyEngine(trigger, user)
    policy_engine.mode = PolicyEngineMode.MODE_ANY
    policy_engine.empty_result = False
    policy_engine.use_cache = False
    policy_engine.request.context["event"] = event
    policy_engine.build()
    result = policy_engine.result
    if not result.passing:
        return

    LOGGER.debug("e(trigger): event trigger matched", trigger=trigger)
    # Create the notification objects
    for transport in trigger.transports.all():
        for user in trigger.group.users.all():
            LOGGER.debug("created notification")
            notification = Notification.objects.create(
                severity=trigger.severity, body=event.summary, event=event, user=user
            )
            notification_transport.apply_async(
                args=[notification.pk, transport.pk], queue="authentik_events"
            )
            if transport.send_once:
                break
Exemple #3
0
 def user_has_access(self, user: Optional[User] = None) -> PolicyResult:
     """Check if user has access to application."""
     user = user or self.request.user
     policy_engine = PolicyEngine(self.application, user or self.request.user, self.request)
     policy_engine.use_cache = False
     policy_engine.build()
     result = policy_engine.result
     LOGGER.debug(
         "PolicyAccessView user_has_access",
         user=user,
         app=self.application,
         result=result,
     )
     if not result.passing:
         for message in result.messages:
             messages.error(self.request, _(message))
     return result
Exemple #4
0
 def process(self, plan: "FlowPlan", stage: Stage,
             http_request: Optional[HttpRequest]) -> Optional[Stage]:
     """Re-evaluate policies bound to stage, and if they fail, remove from plan"""
     engine = PolicyEngine(self.binding, self.user)
     engine.use_cache = False
     if http_request:
         engine.request.set_http_request(http_request)
     engine.request.context = plan.context
     engine.build()
     result = engine.result
     if result.passing:
         return stage
     LOGGER.warning(
         "f(plan_inst)[re-eval marker]: stage failed re-evaluation",
         stage=stage,
         messages=result.messages,
     )
     return None
Exemple #5
0
 def check_access(self, request: Request, slug: str) -> Response:
     """Check access to a single application by slug"""
     # Don't use self.get_object as that checks for view_application permission
     # which the user might not have, even if they have access
     application = get_object_or_404(Application, slug=slug)
     # If the current user is superuser, they can set `for_user`
     for_user = request.user
     if request.user.is_superuser and "for_user" in request.query_params:
         try:
             for_user = get_object_or_404(
                 User, pk=request.query_params.get("for_user"))
         except ValueError:
             return HttpResponseBadRequest("for_user must be numerical")
     engine = PolicyEngine(application, for_user, request)
     engine.use_cache = False
     engine.build()
     result = engine.result
     response = PolicyTestResultSerializer(PolicyResult(False))
     if result.passing:
         response = PolicyTestResultSerializer(PolicyResult(True))
     if request.user.is_superuser:
         response = PolicyTestResultSerializer(result)
     return Response(response.data)