core: applications api: properly select provider (#10373)
* core: applications api: properly select provider
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
* fixup get_launch_url
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
* Reapply "core: applications api: add option to only list apps with launch url (#10336)" (#10370)
This reverts commit 763a19b914
.
* wip
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
* more fixes
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
* make website
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
* remove serializer change
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
---------
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
This commit is contained in:

committed by
GitHub

parent
446a65d56d
commit
b942ae7849
@ -103,7 +103,12 @@ class ApplicationSerializer(ModelSerializer):
|
||||
class ApplicationViewSet(UsedByMixin, ModelViewSet):
|
||||
"""Application Viewset"""
|
||||
|
||||
queryset = Application.objects.all().prefetch_related("provider").prefetch_related("policies")
|
||||
queryset = (
|
||||
Application.objects.all()
|
||||
.with_provider()
|
||||
.prefetch_related("policies")
|
||||
.prefetch_related("backchannel_providers")
|
||||
)
|
||||
serializer_class = ApplicationSerializer
|
||||
search_fields = [
|
||||
"name",
|
||||
@ -147,6 +152,15 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
|
||||
applications.append(application)
|
||||
return applications
|
||||
|
||||
def _filter_applications_with_launch_url(
|
||||
self, pagined_apps: Iterator[Application]
|
||||
) -> list[Application]:
|
||||
applications = []
|
||||
for app in pagined_apps:
|
||||
if app.get_launch_url():
|
||||
applications.append(app)
|
||||
return applications
|
||||
|
||||
@extend_schema(
|
||||
parameters=[
|
||||
OpenApiParameter(
|
||||
@ -204,6 +218,11 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.INT,
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="only_with_launch_url",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.BOOL,
|
||||
),
|
||||
]
|
||||
)
|
||||
def list(self, request: Request) -> Response:
|
||||
@ -216,6 +235,10 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
|
||||
if superuser_full_list and request.user.is_superuser:
|
||||
return super().list(request)
|
||||
|
||||
only_with_launch_url = str(
|
||||
request.query_params.get("only_with_launch_url", "false")
|
||||
).lower()
|
||||
|
||||
queryset = self._filter_queryset_for_list(self.get_queryset())
|
||||
paginator: Pagination = self.paginator
|
||||
paginated_apps = paginator.paginate_queryset(queryset, request)
|
||||
@ -251,6 +274,10 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
|
||||
allowed_applications,
|
||||
timeout=86400,
|
||||
)
|
||||
|
||||
if only_with_launch_url == "true":
|
||||
allowed_applications = self._filter_applications_with_launch_url(allowed_applications)
|
||||
|
||||
serializer = self.get_serializer(allowed_applications, many=True)
|
||||
return self.get_paginated_response(serializer.data)
|
||||
|
||||
|
@ -11,6 +11,7 @@ from django.contrib.auth.models import AbstractUser
|
||||
from django.contrib.auth.models import UserManager as DjangoUserManager
|
||||
from django.db import models
|
||||
from django.db.models import Q, QuerySet, options
|
||||
from django.db.models.constants import LOOKUP_SEP
|
||||
from django.http import HttpRequest
|
||||
from django.utils.functional import SimpleLazyObject, cached_property
|
||||
from django.utils.timezone import now
|
||||
@ -461,6 +462,16 @@ class BackchannelProvider(Provider):
|
||||
abstract = True
|
||||
|
||||
|
||||
class ApplicationQuerySet(QuerySet):
|
||||
def with_provider(self) -> "QuerySet[Application]":
|
||||
qs = self.select_related("provider")
|
||||
for subclass in Provider.objects.get_queryset()._get_subclasses_recurse(Provider):
|
||||
if LOOKUP_SEP in subclass:
|
||||
continue
|
||||
qs = qs.select_related(f"provider__{subclass}")
|
||||
return qs
|
||||
|
||||
|
||||
class Application(SerializerModel, PolicyBindingModel):
|
||||
"""Every Application which uses authentik for authentication/identification/authorization
|
||||
needs an Application record. Other authentication types can subclass this Model to
|
||||
@ -492,6 +503,8 @@ class Application(SerializerModel, PolicyBindingModel):
|
||||
meta_description = models.TextField(default="", blank=True)
|
||||
meta_publisher = models.TextField(default="", blank=True)
|
||||
|
||||
objects = ApplicationQuerySet.as_manager()
|
||||
|
||||
@property
|
||||
def serializer(self) -> Serializer:
|
||||
from authentik.core.api.applications import ApplicationSerializer
|
||||
@ -528,16 +541,19 @@ class Application(SerializerModel, PolicyBindingModel):
|
||||
return url
|
||||
|
||||
def get_provider(self) -> Provider | None:
|
||||
"""Get casted provider instance"""
|
||||
"""Get casted provider instance. Needs Application queryset with_provider"""
|
||||
if not self.provider:
|
||||
return None
|
||||
# if the Application class has been cache, self.provider is set
|
||||
# but doing a direct query lookup will fail.
|
||||
# In that case, just return None
|
||||
try:
|
||||
return Provider.objects.get_subclass(pk=self.provider.pk)
|
||||
except Provider.DoesNotExist:
|
||||
return None
|
||||
|
||||
for subclass in Provider.objects.get_queryset()._get_subclasses_recurse(Provider):
|
||||
# We don't care about recursion, skip nested models
|
||||
if LOOKUP_SEP in subclass:
|
||||
continue
|
||||
try:
|
||||
return getattr(self.provider, subclass)
|
||||
except AttributeError:
|
||||
pass
|
||||
return None
|
||||
|
||||
def __str__(self):
|
||||
return str(self.name)
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""metadata redirect"""
|
||||
|
||||
from django.http import Http404, HttpRequest, HttpResponse
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import reverse
|
||||
from django.views import View
|
||||
|
||||
@ -12,7 +12,9 @@ class MetadataDownload(View):
|
||||
"""Redirect to metadata download"""
|
||||
|
||||
def dispatch(self, request: HttpRequest, application_slug: str) -> HttpResponse:
|
||||
app: Application = get_object_or_404(Application, slug=application_slug)
|
||||
app = Application.objects.filter(slug=application_slug).with_provider().first()
|
||||
if not app:
|
||||
raise Http404
|
||||
provider = app.get_provider()
|
||||
if not provider:
|
||||
raise Http404
|
||||
|
@ -2682,6 +2682,10 @@ paths:
|
||||
name: name
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: only_with_launch_url
|
||||
schema:
|
||||
type: boolean
|
||||
- name: ordering
|
||||
required: false
|
||||
in: query
|
||||
|
@ -70,6 +70,7 @@ export class LibraryPage extends AKElement {
|
||||
ordering: "name",
|
||||
page,
|
||||
pageSize: 100,
|
||||
onlyWithLaunchUrl: true,
|
||||
});
|
||||
|
||||
const applicationListFetch = await coreApi().coreApplicationsList(applicationListParams(1));
|
||||
|
Reference in New Issue
Block a user