Example #1
0
class EventViewSet(ReadOnlyModelViewSet):
    """Event Read-Only Viewset"""

    queryset = Event.objects.all()
    serializer_class = EventSerializer
    ordering = ["-created"]
    search_fields = [
        "event_uuid",
        "user",
        "action",
        "app",
        "context",
        "client_ip",
    ]
    filterset_class = EventsFilter

    @extend_schema(
        methods=["GET"],
        responses={200: EventTopPerUserSerializer(many=True)},
        parameters=[
            OpenApiParameter(
                "top_n",
                type=OpenApiTypes.INT,
                location=OpenApiParameter.QUERY,
                required=False,
            )
        ],
    )
    @action(detail=False, methods=["GET"], pagination_class=None)
    def top_per_user(self, request: Request):
        """Get the top_n events grouped by user count"""
        filtered_action = request.query_params.get("action", EventAction.LOGIN)
        top_n = int(request.query_params.get("top_n", "15"))
        return Response(
            get_objects_for_user(
                request.user, "authentik_events.view_event").filter(
                    action=filtered_action).exclude(
                        context__authorized_application=None).annotate(
                            application=KeyTextTransform(
                                "authorized_application", "context")).annotate(
                                    user_pk=KeyTextTransform("pk", "user")).
            values("application").annotate(
                counted_events=Count("application")).annotate(
                    unique_users=Count("user_pk", distinct=True)).values(
                        "unique_users", "application",
                        "counted_events").order_by("-counted_events")[:top_n])

    @extend_schema(responses={200: TypeCreateSerializer(many=True)})
    @action(detail=False, pagination_class=None, filter_backends=[])
    def actions(self, request: Request) -> Response:
        """Get all actions"""
        data = []
        for value, name in EventAction.choices:
            data.append({
                "name": name,
                "description": "",
                "component": value,
                "model_name": ""
            })
        return Response(TypeCreateSerializer(data, many=True).data)
Example #2
0
class SourceViewSet(
    mixins.RetrieveModelMixin,
    mixins.DestroyModelMixin,
    mixins.ListModelMixin,
    GenericViewSet,
):
    """Source Viewset"""

    queryset = Source.objects.none()
    serializer_class = SourceSerializer
    lookup_field = "slug"

    def get_queryset(self):
        return Source.objects.select_subclasses()

    @swagger_auto_schema(responses={200: TypeCreateSerializer(many=True)})
    @action(detail=False, pagination_class=None, filter_backends=[])
    def types(self, request: Request) -> Response:
        """Get all creatable source types"""
        data = []
        for subclass in all_subclasses(self.queryset.model):
            subclass: Source
            component = ""
            if subclass._meta.abstract:
                component = subclass.__bases__[0]().component
            else:
                component = subclass().component
            # pyright: reportGeneralTypeIssues=false
            data.append(
                {
                    "name": subclass._meta.verbose_name,
                    "description": subclass.__doc__,
                    "component": component,
                    "model_name": subclass._meta.model_name,
                }
            )
        return Response(TypeCreateSerializer(data, many=True).data)

    @swagger_auto_schema(responses={200: UserSettingSerializer(many=True)})
    @action(detail=False, pagination_class=None, filter_backends=[])
    def user_settings(self, request: Request) -> Response:
        """Get all sources the user can configure"""
        _all_sources: Iterable[Source] = Source.objects.filter(
            enabled=True
        ).select_subclasses()
        matching_sources: list[UserSettingSerializer] = []
        for source in _all_sources:
            user_settings = source.ui_user_settings
            if not user_settings:
                continue
            policy_engine = PolicyEngine(source, request.user, request)
            policy_engine.build()
            if not policy_engine.passing:
                continue
            source_settings = source.ui_user_settings
            source_settings.initial_data["object_uid"] = source.slug
            if not source_settings.is_valid():
                LOGGER.warning(source_settings.errors)
            matching_sources.append(source_settings.validated_data)
        return Response(matching_sources)
Example #3
0
 def actions(self, request: Request) -> Response:
     """Get all actions"""
     data = []
     for value, name in EventAction.choices:
         data.append(
             {"name": name, "description": "", "component": value, "model_name": ""}
         )
     return Response(TypeCreateSerializer(data, many=True).data)
Example #4
0
class StageViewSet(
        mixins.RetrieveModelMixin,
        mixins.DestroyModelMixin,
        UsedByMixin,
        mixins.ListModelMixin,
        GenericViewSet,
):
    """Stage Viewset"""

    queryset = Stage.objects.all().select_related("flow_set")
    serializer_class = StageSerializer
    search_fields = ["name"]
    filterset_fields = ["name"]

    def get_queryset(self):  # pragma: no cover
        return Stage.objects.select_subclasses()

    @extend_schema(responses={200: TypeCreateSerializer(many=True)})
    @action(detail=False, pagination_class=None, filter_backends=[])
    def types(self, request: Request) -> Response:
        """Get all creatable stage types"""
        data = []
        for subclass in all_subclasses(self.queryset.model, False):
            subclass: Stage
            data.append({
                "name": subclass._meta.verbose_name,
                "description": subclass.__doc__,
                "component": subclass().component,
                "model_name": subclass._meta.model_name,
            })
        data = sorted(data, key=lambda x: x["name"])
        return Response(TypeCreateSerializer(data, many=True).data)

    @extend_schema(responses={200: UserSettingSerializer(many=True)})
    @action(detail=False, pagination_class=None, filter_backends=[])
    def user_settings(self, request: Request) -> Response:
        """Get all stages the user can configure"""
        stages = []
        for configurable_stage in all_subclasses(ConfigurableStage):
            stages += list(configurable_stage.objects.all().order_by("name"))
        matching_stages: list[dict] = []
        for stage in stages:
            user_settings = stage.ui_user_settings()
            if not user_settings:
                continue
            user_settings.initial_data["object_uid"] = str(stage.pk)
            if hasattr(stage, "configure_flow") and stage.configure_flow:
                user_settings.initial_data["configure_url"] = reverse(
                    "authentik_flows:configure",
                    kwargs={"stage_uuid": stage.pk},
                )
            if not user_settings.is_valid():
                LOGGER.warning(user_settings.errors)
            matching_stages.append(user_settings.initial_data)
        return Response(matching_stages)
Example #5
0
 def templates(self, request: Request) -> Response:
     """Get all available templates, including custom templates"""
     choices = []
     for value, label in get_template_choices():
         choices.append({
             "name": value,
             "description": label,
             "component": "",
             "model_name": "",
         })
     return Response(TypeCreateSerializer(choices, many=True).data)
Example #6
0
 def types(self, request: Request) -> Response:
     """Get all creatable policy types"""
     data = []
     for subclass in all_subclasses(self.queryset.model):
         subclass: Policy
         data.append({
             "name": subclass._meta.verbose_name,
             "description": subclass.__doc__,
             "component": subclass().component,
             "model_name": subclass._meta.model_name,
         })
     return Response(TypeCreateSerializer(data, many=True).data)
Example #7
0
 def types(self, request: Request) -> Response:
     """Get all creatable property-mapping types"""
     data = []
     for subclass in all_subclasses(self.queryset.model):
         subclass: PropertyMapping
         data.append({
             "name": subclass._meta.verbose_name,
             "description": subclass.__doc__,
             # pyright: reportGeneralTypeIssues=false
             "component": subclass().component,
             "model_name": subclass._meta.model_name,
         })
     return Response(TypeCreateSerializer(data, many=True).data)
 def types(self, request: Request) -> Response:
     """Get all creatable service connection types"""
     data = []
     for subclass in all_subclasses(self.queryset.model):
         subclass: OutpostServiceConnection
         # pyright: reportGeneralTypeIssues=false
         data.append({
             "name": subclass._meta.verbose_name,
             "description": subclass.__doc__,
             "component": subclass().component,
             "model_name": subclass._meta.model_name,
         })
     return Response(TypeCreateSerializer(data, many=True).data)
Example #9
0
 def types(self, request: Request) -> Response:
     """Get all creatable stage types"""
     data = []
     for subclass in all_subclasses(self.queryset.model, False):
         subclass: Stage
         data.append({
             "name": subclass._meta.verbose_name,
             "description": subclass.__doc__,
             "component": subclass().component,
             "model_name": subclass._meta.model_name,
         })
     data = sorted(data, key=lambda x: x["name"])
     return Response(TypeCreateSerializer(data, many=True).data)
Example #10
0
class ProviderViewSet(
        mixins.RetrieveModelMixin,
        mixins.DestroyModelMixin,
        UsedByMixin,
        mixins.ListModelMixin,
        GenericViewSet,
):
    """Provider Viewset"""

    queryset = Provider.objects.none()
    serializer_class = ProviderSerializer
    filterset_fields = {
        "application": ["isnull"],
    }
    search_fields = [
        "name",
        "application__name",
    ]

    def get_queryset(self):  # pragma: no cover
        return Provider.objects.select_subclasses()

    @extend_schema(responses={200: TypeCreateSerializer(many=True)})
    @action(detail=False, pagination_class=None, filter_backends=[])
    def types(self, request: Request) -> Response:
        """Get all creatable provider types"""
        data = []
        for subclass in all_subclasses(self.queryset.model):
            subclass: Provider
            data.append({
                "name": subclass._meta.verbose_name,
                "description": subclass.__doc__,
                "component": subclass().component,
                "model_name": subclass._meta.model_name,
            })
        data.append({
            "name":
            _("SAML Provider from Metadata"),
            "description":
            _("Create a SAML Provider by importing its Metadata."),
            "component":
            "ak-provider-saml-import-form",
            "model_name":
            "",
        })
        return Response(TypeCreateSerializer(data, many=True).data)
Example #11
0
 def types(self, request: Request) -> Response:
     """Get all creatable source types"""
     data = []
     for subclass in all_subclasses(self.queryset.model):
         subclass: Source
         component = ""
         if subclass._meta.abstract:
             component = subclass.__bases__[0]().component
         else:
             component = subclass().component
         # pyright: reportGeneralTypeIssues=false
         data.append({
             "name": subclass._meta.verbose_name,
             "description": subclass.__doc__,
             "component": component,
             "model_name": subclass._meta.model_name,
         })
     return Response(TypeCreateSerializer(data, many=True).data)
Example #12
0
 def types(self, request: Request) -> Response:
     """Get all creatable provider types"""
     data = []
     for subclass in all_subclasses(self.queryset.model):
         subclass: Provider
         data.append({
             "name": subclass._meta.verbose_name,
             "description": subclass.__doc__,
             "component": subclass().component,
         })
     data.append({
         "name":
         _("SAML Provider from Metadata"),
         "description":
         _("Create a SAML Provider by importing its Metadata."),
         "component":
         "ak-provider-saml-import-form",
     })
     return Response(TypeCreateSerializer(data, many=True).data)
Example #13
0
class EmailStageViewSet(ModelViewSet):
    """EmailStage Viewset"""

    queryset = EmailStage.objects.all()
    serializer_class = EmailStageSerializer

    @extend_schema(responses={200: TypeCreateSerializer(many=True)})
    @action(detail=False, pagination_class=None, filter_backends=[])
    def templates(self, request: Request) -> Response:
        """Get all available templates, including custom templates"""
        choices = []
        for value, label in get_template_choices():
            choices.append({
                "name": value,
                "description": label,
                "component": "",
                "model_name": "",
            })
        return Response(TypeCreateSerializer(choices, many=True).data)
Example #14
0
class ServiceConnectionViewSet(
        mixins.RetrieveModelMixin,
        mixins.DestroyModelMixin,
        UsedByMixin,
        mixins.ListModelMixin,
        GenericViewSet,
):
    """ServiceConnection Viewset"""

    queryset = OutpostServiceConnection.objects.select_subclasses()
    serializer_class = ServiceConnectionSerializer
    search_fields = ["name"]
    filterset_fields = ["name"]

    @extend_schema(responses={200: TypeCreateSerializer(many=True)})
    @action(detail=False, pagination_class=None, filter_backends=[])
    def types(self, request: Request) -> Response:
        """Get all creatable service connection types"""
        data = []
        for subclass in all_subclasses(self.queryset.model):
            subclass: OutpostServiceConnection
            # pyright: reportGeneralTypeIssues=false
            data.append({
                "name": subclass._meta.verbose_name,
                "description": subclass.__doc__,
                "component": subclass().component,
                "model_name": subclass._meta.model_name,
            })
        return Response(TypeCreateSerializer(data, many=True).data)

    @extend_schema(
        responses={200: ServiceConnectionStateSerializer(many=False)})
    @action(detail=True, pagination_class=None, filter_backends=[])
    # pylint: disable=unused-argument, invalid-name
    def state(self, request: Request, pk: str) -> Response:
        """Get the service connection's state"""
        connection = self.get_object()
        return Response(asdict(connection.state))
Example #15
0
class EmailStageViewSet(UsedByMixin, ModelViewSet):
    """EmailStage Viewset"""

    queryset = EmailStage.objects.all()
    serializer_class = EmailStageSerializer
    filterset_fields = [
        "name",
        "use_global_settings",
        "host",
        "port",
        "username",
        "use_tls",
        "use_ssl",
        "timeout",
        "from_address",
        "token_expiry",
        "subject",
        "template",
        "activate_user_on_success",
    ]
    ordering = ["name"]

    @extend_schema(responses={200: TypeCreateSerializer(many=True)})
    @action(detail=False, pagination_class=None, filter_backends=[])
    def templates(self, request: Request) -> Response:
        """Get all available templates, including custom templates"""
        choices = []
        for value, label in get_template_choices():
            choices.append(
                {
                    "name": value,
                    "description": label,
                    "component": "",
                    "model_name": "",
                }
            )
        return Response(TypeCreateSerializer(choices, many=True).data)
Example #16
0
class PropertyMappingViewSet(
        mixins.RetrieveModelMixin,
        mixins.DestroyModelMixin,
        UsedByMixin,
        mixins.ListModelMixin,
        GenericViewSet,
):
    """PropertyMapping Viewset"""

    queryset = PropertyMapping.objects.none()
    serializer_class = PropertyMappingSerializer
    search_fields = [
        "name",
    ]
    filterset_fields = {"managed": ["isnull"]}
    ordering = ["name"]

    def get_queryset(self):  # pragma: no cover
        return PropertyMapping.objects.select_subclasses()

    @extend_schema(responses={200: TypeCreateSerializer(many=True)})
    @action(detail=False, pagination_class=None, filter_backends=[])
    def types(self, request: Request) -> Response:
        """Get all creatable property-mapping types"""
        data = []
        for subclass in all_subclasses(self.queryset.model):
            subclass: PropertyMapping
            data.append({
                "name": subclass._meta.verbose_name,
                "description": subclass.__doc__,
                # pyright: reportGeneralTypeIssues=false
                "component": subclass().component,
                "model_name": subclass._meta.model_name,
            })
        return Response(TypeCreateSerializer(data, many=True).data)

    @permission_required("authentik_core.view_propertymapping")
    @extend_schema(
        request=PolicyTestSerializer(),
        responses={
            200: PropertyMappingTestResultSerializer,
            400: OpenApiResponse(description="Invalid parameters"),
        },
        parameters=[
            OpenApiParameter(
                name="format_result",
                location=OpenApiParameter.QUERY,
                type=OpenApiTypes.BOOL,
            )
        ],
    )
    @action(detail=True,
            pagination_class=None,
            filter_backends=[],
            methods=["POST"])
    # pylint: disable=unused-argument, invalid-name
    def test(self, request: Request, pk: str) -> Response:
        """Test Property Mapping"""
        mapping: PropertyMapping = self.get_object()
        test_params = PolicyTestSerializer(data=request.data)
        if not test_params.is_valid():
            return Response(test_params.errors, status=400)

        format_result = str(request.GET.get("format_result",
                                            "false")).lower() == "true"

        # User permission check, only allow mapping testing for users that are readable
        users = get_objects_for_user(
            request.user, "authentik_core.view_user").filter(
                pk=test_params.validated_data["user"].pk)
        if not users.exists():
            raise PermissionDenied()

        response_data = {"successful": True, "result": ""}
        try:
            result = mapping.evaluate(
                users.first(),
                self.request,
                **test_params.validated_data.get("context", {}),
            )
            response_data["result"] = dumps(
                result, indent=(4 if format_result else None))
        except Exception as exc:  # pylint: disable=broad-except
            response_data["result"] = str(exc)
            response_data["successful"] = False
        response = PropertyMappingTestResultSerializer(response_data)
        return Response(response.data)
Example #17
0
class PolicyViewSet(
        mixins.RetrieveModelMixin,
        mixins.DestroyModelMixin,
        mixins.ListModelMixin,
        GenericViewSet,
):
    """Policy Viewset"""

    queryset = Policy.objects.all()
    serializer_class = PolicySerializer
    filterset_fields = {
        "bindings": ["isnull"],
        "promptstage": ["isnull"],
    }
    search_fields = ["name"]

    def get_queryset(self):
        return Policy.objects.select_subclasses().prefetch_related(
            "bindings", "promptstage_set")

    @extend_schema(responses={200: TypeCreateSerializer(many=True)})
    @action(detail=False, pagination_class=None, filter_backends=[])
    def types(self, request: Request) -> Response:
        """Get all creatable policy types"""
        data = []
        for subclass in all_subclasses(self.queryset.model):
            subclass: Policy
            data.append({
                "name": subclass._meta.verbose_name,
                "description": subclass.__doc__,
                "component": subclass().component,
                "model_name": subclass._meta.model_name,
            })
        return Response(TypeCreateSerializer(data, many=True).data)

    @permission_required(None, ["authentik_policies.view_policy_cache"])
    @extend_schema(responses={200: CacheSerializer(many=False)})
    @action(detail=False, pagination_class=None, filter_backends=[])
    def cache_info(self, request: Request) -> Response:
        """Info about cached policies"""
        return Response(data={"count": len(cache.keys("policy_*"))})

    @permission_required(None, ["authentik_policies.clear_policy_cache"])
    @extend_schema(
        request=OpenApiTypes.NONE,
        responses={
            204: OpenApiResponse(description="Successfully cleared cache"),
            400: OpenApiResponse(description="Bad request"),
        },
    )
    @action(detail=False, methods=["POST"])
    def cache_clear(self, request: Request) -> Response:
        """Clear policy cache"""
        keys = cache.keys("policy_*")
        cache.delete_many(keys)
        LOGGER.debug("Cleared Policy cache", keys=len(keys))
        # Also delete user application cache
        keys = cache.keys(user_app_cache_key("*"))
        cache.delete_many(keys)
        return Response(status=204)

    @permission_required("authentik_policies.view_policy")
    @extend_schema(
        request=PolicyTestSerializer(),
        responses={
            200: PolicyTestResultSerializer(),
            400: OpenApiResponse(description="Invalid parameters"),
        },
    )
    @action(detail=True,
            pagination_class=None,
            filter_backends=[],
            methods=["POST"])
    # pylint: disable=unused-argument, invalid-name
    def test(self, request: Request, pk: str) -> Response:
        """Test policy"""
        policy = self.get_object()
        test_params = PolicyTestSerializer(data=request.data)
        if not test_params.is_valid():
            return Response(test_params.errors, status=400)

        # User permission check, only allow policy testing for users that are readable
        users = get_objects_for_user(
            request.user, "authentik_core.view_user").filter(
                pk=test_params.validated_data["user"].pk)
        if not users.exists():
            raise PermissionDenied()

        p_request = PolicyRequest(users.first())
        p_request.debug = True
        p_request.set_http_request(self.request)
        p_request.context = test_params.validated_data.get("context", {})

        proc = PolicyProcess(PolicyBinding(policy=policy), p_request, None)
        result = proc.execute()
        response = PolicyTestResultSerializer(result)
        return Response(response.data)