enterprise/search: move QL to open source] (#21484)

* enterprise/search move to /search

* use make gen for schema updates

* update docs

* re-org

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* cleanup more

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix web

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* oops

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* huh

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* typing

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* gen

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Fletcher Heisler
2026-04-09 07:37:11 -07:00
committed by GitHub
parent 92fceb1524
commit c32f21046d
30 changed files with 274 additions and 312 deletions
+28 -3
View File
@@ -1,10 +1,18 @@
"""Pagination which includes total pages and current page"""
from typing import TYPE_CHECKING
from drf_spectacular.plumbing import build_object_type
from rest_framework import pagination
from rest_framework.response import Response
from authentik.api.search.ql import QLSearch
from authentik.api.v3.schema.pagination import PAGINATION
from authentik.api.v3.schema.search import AUTOCOMPLETE_SCHEMA
if TYPE_CHECKING:
from django.db.models import QuerySet
from rest_framework.request import Request
class Pagination(pagination.PageNumberPagination):
@@ -13,14 +21,14 @@ class Pagination(pagination.PageNumberPagination):
page_query_param = "page"
page_size_query_param = "page_size"
def get_page_size(self, request):
def get_page_size(self, request: Request) -> int:
if self.page_size_query_param in request.query_params:
page_size = super().get_page_size(request)
if page_size is not None:
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):
def get_paginated_response(self, data) -> Response:
previous_page_number = 0
if self.page.has_previous():
previous_page_number = self.page.previous_page_number()
@@ -39,16 +47,33 @@ class Pagination(pagination.PageNumberPagination):
"end_index": self.page.end_index(),
},
"results": data,
"autocomplete": self.get_autocomplete(),
}
)
def paginate_queryset(self, queryset: QuerySet, request: Request, view=None):
self.view = view
return super().paginate_queryset(queryset, request, view)
def get_autocomplete(self):
schema = QLSearch().get_schema(self.request, self.view)
introspections = {}
if hasattr(self.view, "get_ql_fields"):
from authentik.api.search.schema import AKQLSchemaSerializer
introspections = AKQLSchemaSerializer().serialize(
schema(self.page.paginator.object_list.model)
)
return introspections
def get_paginated_response_schema(self, schema):
return build_object_type(
properties={
"pagination": PAGINATION.ref,
"results": schema,
"autocomplete": AUTOCOMPLETE_SCHEMA.ref,
},
required=["pagination", "results"],
required=["pagination", "results", "autocomplete"],
)
@@ -1,25 +1,17 @@
"""DjangoQL search"""
from django.apps import apps
from django.db.models import QuerySet
from djangoql.ast import Name
from djangoql.exceptions import DjangoQLError
from djangoql.queryset import apply_search
from djangoql.schema import DjangoQLSchema
from drf_spectacular.plumbing import ResolvedComponent, build_object_type
from rest_framework.filters import SearchFilter
from rest_framework.request import Request
from structlog.stdlib import get_logger
from authentik.enterprise.search.fields import JSONSearchField
from authentik.api.search.fields import JSONSearchField
LOGGER = get_logger()
AUTOCOMPLETE_SCHEMA = ResolvedComponent(
name="Autocomplete",
object="Autocomplete",
type=ResolvedComponent.SCHEMA,
schema=build_object_type(additionalProperties={}),
)
class BaseSchema(DjangoQLSchema):
@@ -48,10 +40,6 @@ class QLSearch(SearchFilter):
super().__init__()
self._fallback = SearchFilter()
@property
def enabled(self):
return apps.get_app_config("authentik_enterprise").enabled()
def get_search_terms(self, request: Request) -> str:
"""Search terms are set by a ?search=... query parameter,
and may be comma and/or whitespace delimited."""
@@ -73,7 +61,7 @@ class QLSearch(SearchFilter):
def filter_queryset(self, request: Request, queryset: QuerySet, view) -> QuerySet:
search_query = self.get_search_terms(request)
schema = self.get_schema(request, view)
if len(search_query) == 0 or not self.enabled:
if len(search_query) == 0:
return self._fallback.filter_queryset(request, queryset, view)
try:
return apply_search(queryset, search_query, schema=schema)
@@ -1,8 +1,6 @@
from djangoql.serializers import DjangoQLSchemaSerializer
from drf_spectacular.generators import SchemaGenerator
from authentik.enterprise.search.fields import JSONSearchField
from authentik.enterprise.search.ql import AUTOCOMPLETE_SCHEMA
from authentik.api.search.fields import JSONSearchField
class AKQLSchemaSerializer(DjangoQLSchemaSerializer):
@@ -20,9 +18,3 @@ class AKQLSchemaSerializer(DjangoQLSchemaSerializer):
if isinstance(field, JSONSearchField):
result["relation"] = field.relation()
return result
def postprocess_schema_search_autocomplete(result, generator: SchemaGenerator, **kwargs):
generator.registry.register_on_missing(AUTOCOMPLETE_SCHEMA)
return result
@@ -1,5 +1,4 @@
from json import loads
from unittest.mock import PropertyMock, patch
from urllib.parse import urlencode
from django.urls import reverse
@@ -8,10 +7,6 @@ from rest_framework.test import APITestCase
from authentik.core.tests.utils import create_test_admin_user
@patch(
"authentik.enterprise.audit.middleware.EnterpriseAuditMiddleware.enabled",
PropertyMock(return_value=True),
)
class QLTest(APITestCase):
def setUp(self):
+20
View File
@@ -0,0 +1,20 @@
from typing import TYPE_CHECKING
from drf_spectacular.plumbing import ResolvedComponent, build_object_type
if TYPE_CHECKING:
from drf_spectacular.generators import SchemaGenerator
AUTOCOMPLETE_SCHEMA = ResolvedComponent(
name="Autocomplete",
object="Autocomplete",
type=ResolvedComponent.SCHEMA,
schema=build_object_type(additionalProperties={}),
)
def postprocess_schema_search_autocomplete(result, generator: SchemaGenerator, **kwargs):
generator.registry.register_on_missing(AUTOCOMPLETE_SCHEMA)
return result
+4 -6
View File
@@ -7,6 +7,7 @@ from django.http import Http404
from django.utils.translation import gettext as _
from django_filters.filters import CharFilter, ModelMultipleChoiceFilter
from django_filters.filterset import FilterSet
from djangoql.schema import BoolField, StrField
from drf_spectacular.utils import (
OpenApiParameter,
OpenApiResponse,
@@ -25,6 +26,9 @@ from rest_framework.serializers import ListSerializer, ValidationError
from rest_framework.viewsets import ModelViewSet
from authentik.api.authentication import TokenAuthentication
from authentik.api.search.fields import (
JSONSearchField,
)
from authentik.api.validation import validate
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import JSONDictField, ModelSerializer, PassiveSerializer
@@ -265,12 +269,6 @@ class GroupViewSet(UsedByMixin, ModelViewSet):
]
def get_ql_fields(self):
from djangoql.schema import BoolField, StrField
from authentik.enterprise.search.fields import (
JSONSearchField,
)
return [
StrField(Group, "name"),
BoolField(Group, "is_superuser", nullable=True),
+5 -7
View File
@@ -22,6 +22,7 @@ from django_filters.filters import (
UUIDFilter,
)
from django_filters.filterset import FilterSet
from djangoql.schema import BoolField, StrField
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import (
OpenApiParameter,
@@ -55,6 +56,10 @@ from rest_framework.viewsets import ModelViewSet
from structlog.stdlib import get_logger
from authentik.api.authentication import TokenAuthentication
from authentik.api.search.fields import (
ChoiceSearchField,
JSONSearchField,
)
from authentik.api.validation import validate
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
from authentik.brands.models import Brand
@@ -524,13 +529,6 @@ class UserViewSet(
]
def get_ql_fields(self):
from djangoql.schema import BoolField, StrField
from authentik.enterprise.search.fields import (
ChoiceSearchField,
JSONSearchField,
)
return [
StrField(User, "username"),
StrField(User, "name"),
-12
View File
@@ -1,12 +0,0 @@
"""Enterprise app config"""
from authentik.enterprise.apps import EnterpriseConfig
class AuthentikEnterpriseSearchConfig(EnterpriseConfig):
"""Enterprise app config"""
name = "authentik.enterprise.search"
label = "authentik_search"
verbose_name = "authentik Enterprise.Search"
default = True
-51
View File
@@ -1,51 +0,0 @@
from rest_framework.response import Response
from authentik.api.pagination import Pagination
from authentik.enterprise.search.ql import AUTOCOMPLETE_SCHEMA, QLSearch
class AutocompletePagination(Pagination):
def paginate_queryset(self, queryset, request, view=None):
self.view = view
return super().paginate_queryset(queryset, request, view)
def get_autocomplete(self):
schema = QLSearch().get_schema(self.request, self.view)
introspections = {}
if hasattr(self.view, "get_ql_fields"):
from authentik.enterprise.search.schema import AKQLSchemaSerializer
introspections = AKQLSchemaSerializer().serialize(
schema(self.page.paginator.object_list.model)
)
return introspections
def get_paginated_response(self, data):
previous_page_number = 0
if self.page.has_previous():
previous_page_number = self.page.previous_page_number()
next_page_number = 0
if self.page.has_next():
next_page_number = self.page.next_page_number()
return Response(
{
"pagination": {
"next": next_page_number,
"previous": previous_page_number,
"count": self.page.paginator.count,
"current": self.page.number,
"total_pages": self.page.paginator.num_pages,
"start_index": self.page.start_index(),
"end_index": self.page.end_index(),
},
"results": data,
"autocomplete": self.get_autocomplete(),
}
)
def get_paginated_response_schema(self, schema):
final_schema = super().get_paginated_response_schema(schema)
final_schema["properties"]["autocomplete"] = AUTOCOMPLETE_SCHEMA.ref
final_schema["required"].append("autocomplete")
return final_schema
-20
View File
@@ -1,20 +0,0 @@
SPECTACULAR_SETTINGS = {
"POSTPROCESSING_HOOKS": [
"authentik.api.v3.schema.response.postprocess_schema_register",
"authentik.api.v3.schema.response.postprocess_schema_responses",
"authentik.api.v3.schema.query.postprocess_schema_query_params",
"authentik.api.v3.schema.cleanup.postprocess_schema_remove_unused",
"authentik.enterprise.search.schema.postprocess_schema_search_autocomplete",
"authentik.api.v3.schema.enum.postprocess_schema_enums",
],
}
REST_FRAMEWORK = {
"DEFAULT_PAGINATION_CLASS": "authentik.enterprise.search.pagination.AutocompletePagination",
"DEFAULT_FILTER_BACKENDS": [
"authentik.enterprise.search.ql.QLSearch",
"authentik.rbac.filters.ObjectFilter",
"django_filters.rest_framework.DjangoFilterBackend",
"rest_framework.filters.OrderingFilter",
],
}
-1
View File
@@ -14,7 +14,6 @@ TENANT_APPS = [
"authentik.enterprise.providers.ssf",
"authentik.enterprise.providers.ws_federation",
"authentik.enterprise.reports",
"authentik.enterprise.search",
"authentik.enterprise.stages.authenticator_endpoint_gdtc",
"authentik.enterprise.stages.mtls",
"authentik.enterprise.stages.source",
+4 -5
View File
@@ -11,6 +11,8 @@ from django.db.models.functions import TruncHour
from django.db.models.query_utils import Q
from django.utils.text import slugify
from django.utils.timezone import now
from djangoql.schema import DateTimeField as QLDateTimeFIeld
from djangoql.schema import IntField, StrField
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter, extend_schema
from guardian.shortcuts import get_objects_for_user
@@ -27,6 +29,7 @@ from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from authentik.api.search.fields import ChoiceSearchField, JSONSearchField
from authentik.api.validation import validate
from authentik.core.api.object_types import TypeCreateSerializer
from authentik.core.api.utils import ModelSerializer, PassiveSerializer
@@ -171,10 +174,6 @@ class EventViewSet(
filterset_class = EventsFilter
def get_ql_fields(self):
from djangoql.schema import DateTimeField, IntField, StrField
from authentik.enterprise.search.fields import ChoiceSearchField, JSONSearchField
return [
ChoiceSearchField(Event, "action"),
StrField(Event, "event_uuid"),
@@ -216,7 +215,7 @@ class EventViewSet(
),
),
),
DateTimeField(Event, "created", suggest_options=True),
QLDateTimeFIeld(Event, "created", suggest_options=True),
]
@extend_schema(
@@ -49,6 +49,7 @@ class TestRBACPermissionRoles(APITestCase):
self.assertJSONEqual(
res.content,
{
"autocomplete": {},
"pagination": {
"next": 0,
"previous": 0,
+2 -1
View File
@@ -208,6 +208,7 @@ SPECTACULAR_SETTINGS = {
"authentik.api.v3.schema.response.postprocess_schema_responses",
"authentik.api.v3.schema.query.postprocess_schema_query_params",
"authentik.api.v3.schema.cleanup.postprocess_schema_remove_unused",
"authentik.api.v3.schema.search.postprocess_schema_search_autocomplete",
"authentik.api.v3.schema.enum.postprocess_schema_enums",
],
}
@@ -215,10 +216,10 @@ SPECTACULAR_SETTINGS = {
REST_FRAMEWORK = {
"DEFAULT_PAGINATION_CLASS": "authentik.api.pagination.Pagination",
"DEFAULT_FILTER_BACKENDS": [
"authentik.api.search.ql.QLSearch",
"authentik.rbac.filters.ObjectFilter",
"django_filters.rest_framework.DjangoFilterBackend",
"rest_framework.filters.OrderingFilter",
"rest_framework.filters.SearchFilter",
],
"DEFAULT_PERMISSION_CLASSES": ("authentik.rbac.permissions.ObjectPermissions",),
"DEFAULT_AUTHENTICATION_CLASSES": (
-1
View File
@@ -8934,7 +8934,6 @@
"authentik.enterprise.providers.ssf",
"authentik.enterprise.providers.ws_federation",
"authentik.enterprise.reports",
"authentik.enterprise.search",
"authentik.enterprise.stages.authenticator_endpoint_gdtc",
"authentik.enterprise.stages.mtls",
"authentik.enterprise.stages.source"
-2
View File
@@ -97,7 +97,6 @@ const (
APPENUM_AUTHENTIK_ENTERPRISE_PROVIDERS_SSF AppEnum = "authentik.enterprise.providers.ssf"
APPENUM_AUTHENTIK_ENTERPRISE_PROVIDERS_WS_FEDERATION AppEnum = "authentik.enterprise.providers.ws_federation"
APPENUM_AUTHENTIK_ENTERPRISE_REPORTS AppEnum = "authentik.enterprise.reports"
APPENUM_AUTHENTIK_ENTERPRISE_SEARCH AppEnum = "authentik.enterprise.search"
APPENUM_AUTHENTIK_ENTERPRISE_STAGES_AUTHENTICATOR_ENDPOINT_GDTC AppEnum = "authentik.enterprise.stages.authenticator_endpoint_gdtc"
APPENUM_AUTHENTIK_ENTERPRISE_STAGES_MTLS AppEnum = "authentik.enterprise.stages.mtls"
APPENUM_AUTHENTIK_ENTERPRISE_STAGES_SOURCE AppEnum = "authentik.enterprise.stages.source"
@@ -181,7 +180,6 @@ var AllowedAppEnumEnumValues = []AppEnum{
"authentik.enterprise.providers.ssf",
"authentik.enterprise.providers.ws_federation",
"authentik.enterprise.reports",
"authentik.enterprise.search",
"authentik.enterprise.stages.authenticator_endpoint_gdtc",
"authentik.enterprise.stages.mtls",
"authentik.enterprise.stages.source",
@@ -23,6 +23,7 @@ var _ MappedNullable = &PaginatedExtraRoleObjectPermissionList{}
type PaginatedExtraRoleObjectPermissionList struct {
Pagination Pagination `json:"pagination"`
Results []ExtraRoleObjectPermission `json:"results"`
Autocomplete map[string]interface{} `json:"autocomplete"`
AdditionalProperties map[string]interface{}
}
@@ -32,10 +33,11 @@ type _PaginatedExtraRoleObjectPermissionList PaginatedExtraRoleObjectPermissionL
// This constructor will assign default values to properties that have it defined,
// and makes sure properties required by API are set, but the set of arguments
// will change when the set of required properties is changed
func NewPaginatedExtraRoleObjectPermissionList(pagination Pagination, results []ExtraRoleObjectPermission) *PaginatedExtraRoleObjectPermissionList {
func NewPaginatedExtraRoleObjectPermissionList(pagination Pagination, results []ExtraRoleObjectPermission, autocomplete map[string]interface{}) *PaginatedExtraRoleObjectPermissionList {
this := PaginatedExtraRoleObjectPermissionList{}
this.Pagination = pagination
this.Results = results
this.Autocomplete = autocomplete
return &this
}
@@ -95,6 +97,30 @@ func (o *PaginatedExtraRoleObjectPermissionList) SetResults(v []ExtraRoleObjectP
o.Results = v
}
// GetAutocomplete returns the Autocomplete field value
func (o *PaginatedExtraRoleObjectPermissionList) GetAutocomplete() map[string]interface{} {
if o == nil {
var ret map[string]interface{}
return ret
}
return o.Autocomplete
}
// GetAutocompleteOk returns a tuple with the Autocomplete field value
// and a boolean to check if the value has been set.
func (o *PaginatedExtraRoleObjectPermissionList) GetAutocompleteOk() (map[string]interface{}, bool) {
if o == nil {
return map[string]interface{}{}, false
}
return o.Autocomplete, true
}
// SetAutocomplete sets field value
func (o *PaginatedExtraRoleObjectPermissionList) SetAutocomplete(v map[string]interface{}) {
o.Autocomplete = v
}
func (o PaginatedExtraRoleObjectPermissionList) MarshalJSON() ([]byte, error) {
toSerialize, err := o.ToMap()
if err != nil {
@@ -107,6 +133,7 @@ func (o PaginatedExtraRoleObjectPermissionList) ToMap() (map[string]interface{},
toSerialize := map[string]interface{}{}
toSerialize["pagination"] = o.Pagination
toSerialize["results"] = o.Results
toSerialize["autocomplete"] = o.Autocomplete
for key, value := range o.AdditionalProperties {
toSerialize[key] = value
@@ -122,6 +149,7 @@ func (o *PaginatedExtraRoleObjectPermissionList) UnmarshalJSON(data []byte) (err
requiredProperties := []string{
"pagination",
"results",
"autocomplete",
}
allProperties := make(map[string]interface{})
@@ -153,6 +181,7 @@ func (o *PaginatedExtraRoleObjectPermissionList) UnmarshalJSON(data []byte) (err
if err = json.Unmarshal(data, &additionalProperties); err == nil {
delete(additionalProperties, "pagination")
delete(additionalProperties, "results")
delete(additionalProperties, "autocomplete")
o.AdditionalProperties = additionalProperties
}
-3
View File
@@ -165,8 +165,6 @@ pub enum AppEnum {
AuthentikEnterpriseProvidersWsFederation,
#[serde(rename = "authentik.enterprise.reports")]
AuthentikEnterpriseReports,
#[serde(rename = "authentik.enterprise.search")]
AuthentikEnterpriseSearch,
#[serde(rename = "authentik.enterprise.stages.authenticator_endpoint_gdtc")]
AuthentikEnterpriseStagesAuthenticatorEndpointGdtc,
#[serde(rename = "authentik.enterprise.stages.mtls")]
@@ -290,7 +288,6 @@ impl std::fmt::Display for AppEnum {
write!(f, "authentik.enterprise.providers.ws_federation")
}
Self::AuthentikEnterpriseReports => write!(f, "authentik.enterprise.reports"),
Self::AuthentikEnterpriseSearch => write!(f, "authentik.enterprise.search"),
Self::AuthentikEnterpriseStagesAuthenticatorEndpointGdtc => {
write!(f, "authentik.enterprise.stages.authenticator_endpoint_gdtc")
}
@@ -16,16 +16,20 @@ pub struct PaginatedExtraRoleObjectPermissionList {
pub pagination: models::Pagination,
#[serde(rename = "results")]
pub results: Vec<models::ExtraRoleObjectPermission>,
#[serde(rename = "autocomplete")]
pub autocomplete: std::collections::HashMap<String, serde_json::Value>,
}
impl PaginatedExtraRoleObjectPermissionList {
pub fn new(
pagination: models::Pagination,
results: Vec<models::ExtraRoleObjectPermission>,
autocomplete: std::collections::HashMap<String, serde_json::Value>,
) -> PaginatedExtraRoleObjectPermissionList {
PaginatedExtraRoleObjectPermissionList {
pagination,
results,
autocomplete,
}
}
}
-1
View File
@@ -94,7 +94,6 @@ export const AppEnum = {
AuthentikEnterpriseProvidersSsf: "authentik.enterprise.providers.ssf",
AuthentikEnterpriseProvidersWsFederation: "authentik.enterprise.providers.ws_federation",
AuthentikEnterpriseReports: "authentik.enterprise.reports",
AuthentikEnterpriseSearch: "authentik.enterprise.search",
AuthentikEnterpriseStagesAuthenticatorEndpointGdtc:
"authentik.enterprise.stages.authenticator_endpoint_gdtc",
AuthentikEnterpriseStagesMtls: "authentik.enterprise.stages.mtls",
@@ -38,6 +38,12 @@ export interface PaginatedExtraRoleObjectPermissionList {
* @memberof PaginatedExtraRoleObjectPermissionList
*/
results: Array<ExtraRoleObjectPermission>;
/**
*
* @type {{ [key: string]: any; }}
* @memberof PaginatedExtraRoleObjectPermissionList
*/
autocomplete: { [key: string]: any };
}
/**
@@ -48,6 +54,7 @@ export function instanceOfPaginatedExtraRoleObjectPermissionList(
): value is PaginatedExtraRoleObjectPermissionList {
if (!("pagination" in value) || value["pagination"] === undefined) return false;
if (!("results" in value) || value["results"] === undefined) return false;
if (!("autocomplete" in value) || value["autocomplete"] === undefined) return false;
return true;
}
@@ -67,6 +74,7 @@ export function PaginatedExtraRoleObjectPermissionListFromJSONTyped(
return {
pagination: PaginationFromJSON(json["pagination"]),
results: (json["results"] as Array<any>).map(ExtraRoleObjectPermissionFromJSON),
autocomplete: json["autocomplete"],
};
}
@@ -87,5 +95,6 @@ export function PaginatedExtraRoleObjectPermissionListToJSONTyped(
return {
pagination: PaginationToJSON(value["pagination"]),
results: (value["results"] as Array<any>).map(ExtraRoleObjectPermissionToJSON),
autocomplete: value["autocomplete"],
};
}
+156 -154
View File
File diff suppressed because it is too large Load Diff
@@ -37,7 +37,7 @@ export class GroupMemberSelectTable extends Table<User> {
public override searchPlaceholder = msg("Search for users by username or display name...");
public override searchLabel = msg("Search Users");
public override label = msg("Select Users");
public overridesupportsQL = true;
public override supportsQL = true;
public override checkbox = true;
public override checkboxChip = true;
+2 -5
View File
@@ -17,7 +17,6 @@ import { GroupResult } from "#common/utils";
import { AKElement } from "#elements/Base";
import { intersectionObserver } from "#elements/decorators/intersection-observer";
import { WithLicenseSummary } from "#elements/mixins/license";
import { WithSession } from "#elements/mixins/session";
import { type TransclusionElement } from "#elements/modals/shared";
import { getURLParam, updateURLParams } from "#elements/router/RouteMatch";
@@ -81,7 +80,7 @@ export interface ColumnOptions {
* @template D An optional `toJSON()` result type.
*/
export abstract class Table<T extends object, D = T>
extends WithLicenseSummary(WithSession(AKElement))
extends WithSession(AKElement)
implements TableLike, TransclusionElement
{
static styles: CSSResult[] = [
@@ -871,10 +870,8 @@ export abstract class Table<T extends object, D = T>
return nothing;
}
const isQL = this.supportsQL && this.hasEnterpriseLicense;
return html`<ak-table-search
class="pf-c-toolbar__item pf-m-search-filter ${isQL ? "ql" : ""}"
class="pf-c-toolbar__item pf-m-search-filter ${this.supportsQL ? "ql" : ""}"
part="toolbar-search"
.defaultValue=${this.search}
label=${ifDefined(this.searchLabel)}
+2 -3
View File
@@ -1,7 +1,6 @@
import "#components/ak-search-ql/index";
import { AKElement } from "#elements/Base";
import { WithLicenseSummary } from "#elements/mixins/license";
import { PaginatedResponse } from "#elements/table/Table";
import { ifPresent } from "#elements/utils/attributes";
@@ -16,7 +15,7 @@ import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-gro
import PFToolbar from "@patternfly/patternfly/components/Toolbar/toolbar.css";
@customElement("ak-table-search")
export class TableSearchForm extends WithLicenseSummary(AKElement) {
export class TableSearchForm extends AKElement {
@property({ type: String, reflect: false })
public defaultValue?: string;
@@ -111,7 +110,7 @@ export class TableSearchForm extends WithLicenseSummary(AKElement) {
};
protected renderInput(): TemplateResult {
if (this.supportsQL && this.hasEnterpriseLicense) {
if (this.supportsQL) {
return html`<ak-search-ql
label=${ifPresent(this.label)}
role="presentation"
@@ -31,10 +31,6 @@ View recent events on both a world map view with pinpoints indicating where each
You can export authentik event logs to a CSV file.
#### [Advanced queries](../sys-mgmt/events/logging-events.mdx#advanced-queries)
Allows you to construct advanced queries to find specific event logs using syntax similar to DjangoQL.
### [Google Workspace integration](../add-secure-apps/providers/gws/index.md)
The Google Workspace provider syncs users and groups from authentik to Google Workspace, making authentik the source of truth. It supports direct syncs for real-time changes and automatically linking existing entities.
@@ -47,7 +47,7 @@ You can export your authentik instance's events to a CSV file. To generate a dat
To review, download, or delete past data exports, navigate to **Events** > **Data Exports** in the Admin interface.
## Advanced queries for event logs :ak-enterprise {#advanced-queries}
## Advanced queries for event logs {#advanced-queries}
You can construct advanced queries to find specific event logs. In the Admin interface, navigate to **Events** > **Logs**, and then use the auto-complete in the **Search** field or enter your own queries to return results with greater specificity.
@@ -84,7 +84,7 @@ brand.name = "my brand"
```
```sh Search event by user
user.username in ["ana", "akadmin"]
user.username in ("ana", "akadmin")
```
For more examples, refer to the list of [Event actions](./event-actions.md) and the related examples for each type of event.
@@ -33,7 +33,7 @@ You should see a confirmation pop-up on the top-right of the screen that the use
To create a super-user, you need to add the user to a group that has super-user permissions. For more information, refer to [Create a Group](../groups/manage_groups.mdx#create-a-group).
:::
## Advanced queries for users:ak-enterprise {#advanced-queries}
## Advanced queries for users {#advanced-queries}
You can create advanced queries to locate specific users within the list shown under **Directory** > **Users** in the Admin interface. Use the auto-complete in the **Search** field or enter your own queries to return results with greater specificity.