diff --git a/authentik/api/pagination.py b/authentik/api/pagination.py index 4acd857ab6..8a5c2842cc 100644 --- a/authentik/api/pagination.py +++ b/authentik/api/pagination.py @@ -13,6 +13,11 @@ class Pagination(pagination.PageNumberPagination): page_query_param = "page" page_size_query_param = "page_size" + def get_page_size(self, request): + if self.page_size_query_param in request.query_params: + return min(super().get_page_size(request), request.tenant.pagination_max_page_size) + return request.tenant.pagination_default_page_size + def get_paginated_response(self, data): previous_page_number = 0 if self.page.has_previous(): diff --git a/authentik/root/settings.py b/authentik/root/settings.py index 35b0e1b80f..a544d02af1 100644 --- a/authentik/root/settings.py +++ b/authentik/root/settings.py @@ -207,7 +207,6 @@ SPECTACULAR_SETTINGS = { REST_FRAMEWORK = { "DEFAULT_PAGINATION_CLASS": "authentik.api.pagination.Pagination", - "PAGE_SIZE": 100, "DEFAULT_FILTER_BACKENDS": [ "authentik.rbac.filters.ObjectFilter", "django_filters.rest_framework.DjangoFilterBackend", diff --git a/authentik/sources/scim/views/v2/base.py b/authentik/sources/scim/views/v2/base.py index 94d13dceae..f1102b9ef1 100644 --- a/authentik/sources/scim/views/v2/base.py +++ b/authentik/sources/scim/views/v2/base.py @@ -3,7 +3,6 @@ from typing import Any from uuid import UUID -from django.conf import settings from django.core.paginator import Page, Paginator from django.db.models import Q, QuerySet from django.http import HttpRequest @@ -86,10 +85,9 @@ class SCIMView(APIView): ) def paginate_query(self, query: QuerySet) -> Page: - per_page = 50 + per_page = int(self.request.tenant.pagination_default_page_size) start_index = 1 try: - per_page = int(settings.REST_FRAMEWORK["PAGE_SIZE"]) start_index = int(self.request.query_params.get("startIndex", 1)) except ValueError: pass diff --git a/authentik/sources/scim/views/v2/service_provider_config.py b/authentik/sources/scim/views/v2/service_provider_config.py index 3b2c720979..5ab5c1d506 100644 --- a/authentik/sources/scim/views/v2/service_provider_config.py +++ b/authentik/sources/scim/views/v2/service_provider_config.py @@ -37,7 +37,7 @@ class ServiceProviderConfigView(SCIMView): "bulk": {"supported": False, "maxOperations": 0, "maxPayloadSize": 0}, "filter": { "supported": True, - "maxResults": int(settings.REST_FRAMEWORK["PAGE_SIZE"]), + "maxResults": request.tenant.pagination_default_page_size, }, "changePassword": {"supported": False}, "sort": {"supported": False}, diff --git a/authentik/tenants/api/settings.py b/authentik/tenants/api/settings.py index cad8bcd8d2..3eabb0a4d2 100644 --- a/authentik/tenants/api/settings.py +++ b/authentik/tenants/api/settings.py @@ -69,6 +69,8 @@ class SettingsSerializer(ModelSerializer): "impersonation_require_reason", "default_token_duration", "default_token_length", + "pagination_default_page_size", + "pagination_max_page_size", "flags", ] diff --git a/authentik/tenants/migrations/0007_tenant_pagination_default_page_size_and_more.py b/authentik/tenants/migrations/0007_tenant_pagination_default_page_size_and_more.py new file mode 100644 index 0000000000..e4b2f275cc --- /dev/null +++ b/authentik/tenants/migrations/0007_tenant_pagination_default_page_size_and_more.py @@ -0,0 +1,26 @@ +# Generated by Django 5.2.8 on 2025-12-10 03:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("authentik_tenants", "0006_tenant_flags"), + ] + + operations = [ + migrations.AddField( + model_name="tenant", + name="pagination_default_page_size", + field=models.PositiveIntegerField( + default=20, + help_text="Default page size for API responses, if no size was requested.", + ), + ), + migrations.AddField( + model_name="tenant", + name="pagination_max_page_size", + field=models.PositiveIntegerField(default=100, help_text="Maximum page size"), + ), + ] diff --git a/authentik/tenants/models.py b/authentik/tenants/models.py index f9c3dddf50..8108b0e60c 100644 --- a/authentik/tenants/models.py +++ b/authentik/tenants/models.py @@ -113,6 +113,16 @@ class Tenant(TenantMixin, SerializerModel): default=DEFAULT_TOKEN_LENGTH, validators=[MinValueValidator(1)], ) + + pagination_default_page_size = models.PositiveIntegerField( + help_text=_("Default page size for API responses, if no size was requested."), + default=20, + ) + pagination_max_page_size = models.PositiveIntegerField( + help_text=_("Maximum page size"), + default=100, + ) + flags = models.JSONField(default=dict) def save(self, *args, **kwargs): diff --git a/schema.yml b/schema.yml index 0bbb9ba30f..02b5385d92 100644 --- a/schema.yml +++ b/schema.yml @@ -48744,6 +48744,16 @@ components: maximum: 2147483647 minimum: 1 description: Default token length + pagination_default_page_size: + type: integer + maximum: 2147483647 + minimum: 0 + description: Default page size for API responses, if no size was requested. + pagination_max_page_size: + type: integer + maximum: 2147483647 + minimum: 0 + description: Maximum page size flags: type: object properties: @@ -53235,6 +53245,16 @@ components: maximum: 2147483647 minimum: 1 description: Default token length + pagination_default_page_size: + type: integer + maximum: 2147483647 + minimum: 0 + description: Default page size for API responses, if no size was requested. + pagination_max_page_size: + type: integer + maximum: 2147483647 + minimum: 0 + description: Maximum page size flags: type: object properties: @@ -53300,6 +53320,16 @@ components: maximum: 2147483647 minimum: 1 description: Default token length + pagination_default_page_size: + type: integer + maximum: 2147483647 + minimum: 0 + description: Default page size for API responses, if no size was requested. + pagination_max_page_size: + type: integer + maximum: 2147483647 + minimum: 0 + description: Maximum page size flags: type: object properties: diff --git a/web/src/admin/admin-settings/AdminSettingsForm.ts b/web/src/admin/admin-settings/AdminSettingsForm.ts index 401b232bd3..91eb875d71 100644 --- a/web/src/admin/admin-settings/AdminSettingsForm.ts +++ b/web/src/admin/admin-settings/AdminSettingsForm.ts @@ -26,6 +26,8 @@ import PFList from "@patternfly/patternfly/components/List/list.css"; const DEFAULT_REPUTATION_LOWER_LIMIT = -5; const DEFAULT_REPUTATION_UPPER_LIMIT = 5; +const DEFAULT_PAGE_SIZE = 20; +const DEFAULT_PAGE_MAX = 100; @customElement("ak-admin-settings-form") export class AdminSettingsForm extends Form { @@ -245,6 +247,20 @@ export class AdminSettingsForm extends Form { value="${settings.defaultTokenLength ?? 60}" help=${msg("Default length of generated tokens")} > + +