Skip to content

Commit

Permalink
core: applications api: properly select provider (#10373)
Browse files Browse the repository at this point in the history
* 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 763a19b.

* 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>
  • Loading branch information
rissson authored Aug 5, 2024
1 parent 446a65d commit b942ae7
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 11 deletions.
29 changes: 28 additions & 1 deletion authentik/core/api/applications.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,12 @@ class Meta:
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",
Expand Down Expand Up @@ -147,6 +152,15 @@ def _get_allowed_applications(
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(
Expand Down Expand Up @@ -204,6 +218,11 @@ def check_access(self, request: Request, slug: str) -> Response:
location=OpenApiParameter.QUERY,
type=OpenApiTypes.INT,
),
OpenApiParameter(
name="only_with_launch_url",
location=OpenApiParameter.QUERY,
type=OpenApiTypes.BOOL,
),
]
)
def list(self, request: Request) -> Response:
Expand All @@ -216,6 +235,10 @@ def list(self, request: Request) -> Response:
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)
Expand Down Expand Up @@ -251,6 +274,10 @@ def list(self, request: Request) -> Response:
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)

Expand Down
32 changes: 24 additions & 8 deletions authentik/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
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
Expand Down Expand Up @@ -461,6 +462,16 @@ class Meta:
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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -528,16 +541,19 @@ def get_launch_url(self, user: Optional["User"] = None) -> str | None:
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)
Expand Down
6 changes: 4 additions & 2 deletions authentik/providers/saml/views/metadata.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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
Expand Down
4 changes: 4 additions & 0 deletions schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions web/src/user/LibraryPage/ak-library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export class LibraryPage extends AKElement {
ordering: "name",
page,
pageSize: 100,
onlyWithLaunchUrl: true,
});

const applicationListFetch = await coreApi().coreApplicationsList(applicationListParams(1));
Expand Down

0 comments on commit b942ae7

Please sign in to comment.