core: add primitives for source property mappings (#10651)
This commit is contained in:

committed by
GitHub

parent
ecd6c0a4d8
commit
45e464368e
@ -2,8 +2,15 @@
|
||||
|
||||
from json import dumps
|
||||
|
||||
from django_filters.filters import AllValuesMultipleFilter, BooleanFilter
|
||||
from django_filters.filterset import FilterSet
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema
|
||||
from drf_spectacular.utils import (
|
||||
OpenApiParameter,
|
||||
OpenApiResponse,
|
||||
extend_schema,
|
||||
extend_schema_field,
|
||||
)
|
||||
from guardian.shortcuts import get_objects_for_user
|
||||
from rest_framework import mixins
|
||||
from rest_framework.decorators import action
|
||||
@ -67,6 +74,18 @@ class PropertyMappingSerializer(ManagedSerializer, ModelSerializer, MetaNameSeri
|
||||
]
|
||||
|
||||
|
||||
class PropertyMappingFilterSet(FilterSet):
|
||||
"""Filter for PropertyMapping"""
|
||||
|
||||
managed = extend_schema_field(OpenApiTypes.STR)(AllValuesMultipleFilter(field_name="managed"))
|
||||
|
||||
managed__isnull = BooleanFilter(field_name="managed", lookup_expr="isnull")
|
||||
|
||||
class Meta:
|
||||
model = PropertyMapping
|
||||
fields = ["name", "managed"]
|
||||
|
||||
|
||||
class PropertyMappingViewSet(
|
||||
TypesMixin,
|
||||
mixins.RetrieveModelMixin,
|
||||
@ -87,11 +106,9 @@ class PropertyMappingViewSet(
|
||||
|
||||
queryset = PropertyMapping.objects.select_subclasses()
|
||||
serializer_class = PropertyMappingSerializer
|
||||
search_fields = [
|
||||
"name",
|
||||
]
|
||||
filterset_fields = {"managed": ["isnull"]}
|
||||
filterset_class = PropertyMappingFilterSet
|
||||
ordering = ["name"]
|
||||
search_fields = ["name"]
|
||||
|
||||
@permission_required("authentik_core.view_propertymapping")
|
||||
@extend_schema(
|
||||
|
@ -28,6 +28,7 @@ from authentik.core.types import UILoginButton, UserSettingSerializer
|
||||
from authentik.lib.avatars import get_avatar
|
||||
from authentik.lib.expression.exceptions import ControlFlowException
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.lib.merge import MERGE_LIST_UNIQUE
|
||||
from authentik.lib.models import (
|
||||
CreatedUpdatedModel,
|
||||
DomainlessFormattedURLValidator,
|
||||
@ -100,6 +101,38 @@ class UserTypes(models.TextChoices):
|
||||
INTERNAL_SERVICE_ACCOUNT = "internal_service_account"
|
||||
|
||||
|
||||
class AttributesMixin(models.Model):
|
||||
"""Adds an attributes property to a model"""
|
||||
|
||||
attributes = models.JSONField(default=dict, blank=True)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def update_attributes(self, properties: dict[str, Any]):
|
||||
"""Update fields and attributes, but correctly by merging dicts"""
|
||||
for key, value in properties.items():
|
||||
if key == "attributes":
|
||||
continue
|
||||
setattr(self, key, value)
|
||||
final_attributes = {}
|
||||
MERGE_LIST_UNIQUE.merge(final_attributes, self.attributes)
|
||||
MERGE_LIST_UNIQUE.merge(final_attributes, properties.get("attributes", {}))
|
||||
self.attributes = final_attributes
|
||||
self.save()
|
||||
|
||||
@classmethod
|
||||
def update_or_create_attributes(
|
||||
cls, query: dict[str, Any], properties: dict[str, Any]
|
||||
) -> tuple[models.Model, bool]:
|
||||
"""Same as django's update_or_create but correctly updates attributes by merging dicts"""
|
||||
instance = cls.objects.filter(**query).first()
|
||||
if not instance:
|
||||
return cls.objects.create(**properties), True
|
||||
instance.update_attributes(properties)
|
||||
return instance, False
|
||||
|
||||
|
||||
class GroupQuerySet(CTEQuerySet):
|
||||
def with_children_recursive(self):
|
||||
"""Recursively get all groups that have the current queryset as parents
|
||||
@ -134,7 +167,7 @@ class GroupQuerySet(CTEQuerySet):
|
||||
return cte.join(Group, group_uuid=cte.col.group_uuid).with_cte(cte)
|
||||
|
||||
|
||||
class Group(SerializerModel):
|
||||
class Group(SerializerModel, AttributesMixin):
|
||||
"""Group model which supports a basic hierarchy and has attributes"""
|
||||
|
||||
group_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
@ -154,10 +187,27 @@ class Group(SerializerModel):
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="children",
|
||||
)
|
||||
attributes = models.JSONField(default=dict, blank=True)
|
||||
|
||||
objects = GroupQuerySet.as_manager()
|
||||
|
||||
class Meta:
|
||||
unique_together = (
|
||||
(
|
||||
"name",
|
||||
"parent",
|
||||
),
|
||||
)
|
||||
indexes = [models.Index(fields=["name"])]
|
||||
verbose_name = _("Group")
|
||||
verbose_name_plural = _("Groups")
|
||||
permissions = [
|
||||
("add_user_to_group", _("Add user to group")),
|
||||
("remove_user_from_group", _("Remove user from group")),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"Group {self.name}"
|
||||
|
||||
@property
|
||||
def serializer(self) -> Serializer:
|
||||
from authentik.core.api.groups import GroupSerializer
|
||||
@ -182,24 +232,6 @@ class Group(SerializerModel):
|
||||
qs = Group.objects.filter(group_uuid=self.group_uuid)
|
||||
return qs.with_children_recursive()
|
||||
|
||||
def __str__(self):
|
||||
return f"Group {self.name}"
|
||||
|
||||
class Meta:
|
||||
unique_together = (
|
||||
(
|
||||
"name",
|
||||
"parent",
|
||||
),
|
||||
)
|
||||
indexes = [models.Index(fields=["name"])]
|
||||
verbose_name = _("Group")
|
||||
verbose_name_plural = _("Groups")
|
||||
permissions = [
|
||||
("add_user_to_group", _("Add user to group")),
|
||||
("remove_user_from_group", _("Remove user from group")),
|
||||
]
|
||||
|
||||
|
||||
class UserQuerySet(models.QuerySet):
|
||||
"""User queryset"""
|
||||
@ -225,7 +257,7 @@ class UserManager(DjangoUserManager):
|
||||
return self.get_queryset().exclude_anonymous()
|
||||
|
||||
|
||||
class User(SerializerModel, GuardianUserMixin, AbstractUser):
|
||||
class User(SerializerModel, GuardianUserMixin, AttributesMixin, AbstractUser):
|
||||
"""authentik User model, based on django's contrib auth user model."""
|
||||
|
||||
uuid = models.UUIDField(default=uuid4, editable=False, unique=True)
|
||||
@ -241,6 +273,28 @@ class User(SerializerModel, GuardianUserMixin, AbstractUser):
|
||||
|
||||
objects = UserManager()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("User")
|
||||
verbose_name_plural = _("Users")
|
||||
permissions = [
|
||||
("reset_user_password", _("Reset Password")),
|
||||
("impersonate", _("Can impersonate other users")),
|
||||
("assign_user_permissions", _("Can assign permissions to users")),
|
||||
("unassign_user_permissions", _("Can unassign permissions from users")),
|
||||
("preview_user", _("Can preview user data sent to providers")),
|
||||
("view_user_applications", _("View applications the user has access to")),
|
||||
]
|
||||
indexes = [
|
||||
models.Index(fields=["last_login"]),
|
||||
models.Index(fields=["password_change_date"]),
|
||||
models.Index(fields=["uuid"]),
|
||||
models.Index(fields=["path"]),
|
||||
models.Index(fields=["type"]),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return self.username
|
||||
|
||||
@staticmethod
|
||||
def default_path() -> str:
|
||||
"""Get the default user path"""
|
||||
@ -322,25 +376,6 @@ class User(SerializerModel, GuardianUserMixin, AbstractUser):
|
||||
"""Get avatar, depending on authentik.avatar setting"""
|
||||
return get_avatar(self)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("User")
|
||||
verbose_name_plural = _("Users")
|
||||
permissions = [
|
||||
("reset_user_password", _("Reset Password")),
|
||||
("impersonate", _("Can impersonate other users")),
|
||||
("assign_user_permissions", _("Can assign permissions to users")),
|
||||
("unassign_user_permissions", _("Can unassign permissions from users")),
|
||||
("preview_user", _("Can preview user data sent to providers")),
|
||||
("view_user_applications", _("View applications the user has access to")),
|
||||
]
|
||||
indexes = [
|
||||
models.Index(fields=["last_login"]),
|
||||
models.Index(fields=["password_change_date"]),
|
||||
models.Index(fields=["uuid"]),
|
||||
models.Index(fields=["path"]),
|
||||
models.Index(fields=["type"]),
|
||||
]
|
||||
|
||||
|
||||
class Provider(SerializerModel):
|
||||
"""Application-independent Provider instance. For example SAML2 Remote, OAuth2 Application"""
|
||||
|
@ -1,14 +1,10 @@
|
||||
"""OAuth2Provider API Views"""
|
||||
|
||||
from django_filters.filters import AllValuesMultipleFilter
|
||||
from django_filters.filterset import FilterSet
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
from rest_framework.fields import CharField
|
||||
from rest_framework.serializers import ValidationError
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from authentik.core.api.property_mappings import PropertyMappingSerializer
|
||||
from authentik.core.api.property_mappings import PropertyMappingFilterSet, PropertyMappingSerializer
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.providers.oauth2.models import ScopeMapping
|
||||
|
||||
@ -33,14 +29,12 @@ class ScopeMappingSerializer(PropertyMappingSerializer):
|
||||
]
|
||||
|
||||
|
||||
class ScopeMappingFilter(FilterSet):
|
||||
class ScopeMappingFilter(PropertyMappingFilterSet):
|
||||
"""Filter for ScopeMapping"""
|
||||
|
||||
managed = extend_schema_field(OpenApiTypes.STR)(AllValuesMultipleFilter(field_name="managed"))
|
||||
|
||||
class Meta:
|
||||
class Meta(PropertyMappingFilterSet.Meta):
|
||||
model = ScopeMapping
|
||||
fields = ["scope_name", "name", "managed"]
|
||||
fields = PropertyMappingFilterSet.Meta.fields + ["scope_name"]
|
||||
|
||||
|
||||
class ScopeMappingViewSet(UsedByMixin, ModelViewSet):
|
||||
|
@ -1,12 +1,8 @@
|
||||
"""Radius Property mappings API Views"""
|
||||
|
||||
from django_filters.filters import AllValuesMultipleFilter
|
||||
from django_filters.filterset import FilterSet
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from authentik.core.api.property_mappings import PropertyMappingSerializer
|
||||
from authentik.core.api.property_mappings import PropertyMappingFilterSet, PropertyMappingSerializer
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.providers.radius.models import RadiusProviderPropertyMapping
|
||||
|
||||
@ -19,14 +15,11 @@ class RadiusProviderPropertyMappingSerializer(PropertyMappingSerializer):
|
||||
fields = PropertyMappingSerializer.Meta.fields
|
||||
|
||||
|
||||
class RadiusProviderPropertyMappingFilter(FilterSet):
|
||||
class RadiusProviderPropertyMappingFilter(PropertyMappingFilterSet):
|
||||
"""Filter for RadiusProviderPropertyMapping"""
|
||||
|
||||
managed = extend_schema_field(OpenApiTypes.STR)(AllValuesMultipleFilter(field_name="managed"))
|
||||
|
||||
class Meta:
|
||||
class Meta(PropertyMappingFilterSet.Meta):
|
||||
model = RadiusProviderPropertyMapping
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class RadiusProviderPropertyMappingViewSet(UsedByMixin, ModelViewSet):
|
||||
|
@ -1,12 +1,8 @@
|
||||
"""SAML Property mappings API Views"""
|
||||
|
||||
from django_filters.filters import AllValuesMultipleFilter
|
||||
from django_filters.filterset import FilterSet
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from authentik.core.api.property_mappings import PropertyMappingSerializer
|
||||
from authentik.core.api.property_mappings import PropertyMappingFilterSet, PropertyMappingSerializer
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.providers.saml.models import SAMLPropertyMapping
|
||||
|
||||
@ -22,14 +18,11 @@ class SAMLPropertyMappingSerializer(PropertyMappingSerializer):
|
||||
]
|
||||
|
||||
|
||||
class SAMLPropertyMappingFilter(FilterSet):
|
||||
class SAMLPropertyMappingFilter(PropertyMappingFilterSet):
|
||||
"""Filter for SAMLPropertyMapping"""
|
||||
|
||||
managed = extend_schema_field(OpenApiTypes.STR)(AllValuesMultipleFilter(field_name="managed"))
|
||||
|
||||
class Meta:
|
||||
class Meta(PropertyMappingFilterSet.Meta):
|
||||
model = SAMLPropertyMapping
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class SAMLPropertyMappingViewSet(UsedByMixin, ModelViewSet):
|
||||
|
@ -1,12 +1,8 @@
|
||||
"""scim Property mappings API Views"""
|
||||
|
||||
from django_filters.filters import AllValuesMultipleFilter
|
||||
from django_filters.filterset import FilterSet
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from authentik.core.api.property_mappings import PropertyMappingSerializer
|
||||
from authentik.core.api.property_mappings import PropertyMappingFilterSet, PropertyMappingSerializer
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.providers.scim.models import SCIMMapping
|
||||
|
||||
@ -19,14 +15,11 @@ class SCIMMappingSerializer(PropertyMappingSerializer):
|
||||
fields = PropertyMappingSerializer.Meta.fields
|
||||
|
||||
|
||||
class SCIMMappingFilter(FilterSet):
|
||||
class SCIMMappingFilter(PropertyMappingFilterSet):
|
||||
"""Filter for SCIMMapping"""
|
||||
|
||||
managed = extend_schema_field(OpenApiTypes.STR)(AllValuesMultipleFilter(field_name="managed"))
|
||||
|
||||
class Meta:
|
||||
class Meta(PropertyMappingFilterSet.Meta):
|
||||
model = SCIMMapping
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class SCIMMappingViewSet(UsedByMixin, ModelViewSet):
|
||||
|
@ -3,10 +3,7 @@
|
||||
from typing import Any
|
||||
|
||||
from django.core.cache import cache
|
||||
from django_filters.filters import AllValuesMultipleFilter
|
||||
from django_filters.filterset import FilterSet
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_field, inline_serializer
|
||||
from drf_spectacular.utils import extend_schema, inline_serializer
|
||||
from guardian.shortcuts import get_objects_for_user
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.exceptions import ValidationError
|
||||
@ -16,7 +13,7 @@ from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from authentik.core.api.property_mappings import PropertyMappingSerializer
|
||||
from authentik.core.api.property_mappings import PropertyMappingFilterSet, PropertyMappingSerializer
|
||||
from authentik.core.api.sources import SourceSerializer
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.crypto.models import CertificateKeyPair
|
||||
@ -185,14 +182,11 @@ class LDAPSourcePropertyMappingSerializer(PropertyMappingSerializer):
|
||||
fields = PropertyMappingSerializer.Meta.fields
|
||||
|
||||
|
||||
class LDAPSourcePropertyMappingFilter(FilterSet):
|
||||
class LDAPSourcePropertyMappingFilter(PropertyMappingFilterSet):
|
||||
"""Filter for LDAPSourcePropertyMapping"""
|
||||
|
||||
managed = extend_schema_field(OpenApiTypes.STR)(AllValuesMultipleFilter(field_name="managed"))
|
||||
|
||||
class Meta:
|
||||
class Meta(PropertyMappingFilterSet.Meta):
|
||||
model = LDAPSourcePropertyMapping
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class LDAPSourcePropertyMappingViewSet(UsedByMixin, ModelViewSet):
|
||||
|
@ -1,16 +1,13 @@
|
||||
"""Sync LDAP Users and groups into authentik"""
|
||||
|
||||
from collections.abc import Generator
|
||||
from typing import Any
|
||||
|
||||
from django.conf import settings
|
||||
from django.db.models.base import Model
|
||||
from ldap3 import DEREF_ALWAYS, SUBTREE, Connection
|
||||
from structlog.stdlib import BoundLogger, get_logger
|
||||
|
||||
from authentik.core.sources.mapper import SourceMapper
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.merge import MERGE_LIST_UNIQUE
|
||||
from authentik.lib.sync.mapper import PropertyMappingManager
|
||||
from authentik.sources.ldap.models import LDAPSource
|
||||
|
||||
@ -122,24 +119,3 @@ class BaseLDAPSynchronizer:
|
||||
except KeyError:
|
||||
cookie = None
|
||||
yield self._connection.response
|
||||
|
||||
def update_or_create_attributes(
|
||||
self,
|
||||
obj: type[Model],
|
||||
query: dict[str, Any],
|
||||
data: dict[str, Any],
|
||||
) -> tuple[Model, bool]:
|
||||
"""Same as django's update_or_create but correctly update attributes by merging dicts"""
|
||||
instance = obj.objects.filter(**query).first()
|
||||
if not instance:
|
||||
return (obj.objects.create(**data), True)
|
||||
for key, value in data.items():
|
||||
if key == "attributes":
|
||||
continue
|
||||
setattr(instance, key, value)
|
||||
final_attributes = {}
|
||||
MERGE_LIST_UNIQUE.merge(final_attributes, instance.attributes)
|
||||
MERGE_LIST_UNIQUE.merge(final_attributes, data.get("attributes", {}))
|
||||
instance.attributes = final_attributes
|
||||
instance.save()
|
||||
return (instance, False)
|
||||
|
@ -78,8 +78,7 @@ class GroupLDAPSynchronizer(BaseLDAPSynchronizer):
|
||||
# Special check for `users` field, as this is an M2M relation, and cannot be sync'd
|
||||
if "users" in defaults:
|
||||
del defaults["users"]
|
||||
ak_group, created = self.update_or_create_attributes(
|
||||
Group,
|
||||
ak_group, created = Group.update_or_create_attributes(
|
||||
{
|
||||
f"attributes__{LDAP_UNIQUENESS}": uniq,
|
||||
},
|
||||
|
@ -78,8 +78,8 @@ class UserLDAPSynchronizer(BaseLDAPSynchronizer):
|
||||
self._logger.debug("Writing user with attributes", **defaults)
|
||||
if "username" not in defaults:
|
||||
raise IntegrityError("Username was not set by propertymappings")
|
||||
ak_user, created = self.update_or_create_attributes(
|
||||
User, {f"attributes__{LDAP_UNIQUENESS}": uniq}, defaults
|
||||
ak_user, created = User.update_or_create_attributes(
|
||||
{f"attributes__{LDAP_UNIQUENESS}": uniq}, defaults
|
||||
)
|
||||
except PropertyMappingExpressionException as exc:
|
||||
raise StopSync(exc, None, exc.mapping) from exc
|
||||
|
76
schema.yml
76
schema.yml
@ -13187,10 +13187,22 @@ paths:
|
||||
operationId: propertymappings_all_list
|
||||
description: PropertyMapping Viewset
|
||||
parameters:
|
||||
- in: query
|
||||
name: managed
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
explode: true
|
||||
style: form
|
||||
- in: query
|
||||
name: managed__isnull
|
||||
schema:
|
||||
type: boolean
|
||||
- in: query
|
||||
name: name
|
||||
schema:
|
||||
type: string
|
||||
- name: ordering
|
||||
required: false
|
||||
in: query
|
||||
@ -14532,10 +14544,6 @@ paths:
|
||||
operationId: propertymappings_radius_list
|
||||
description: RadiusProviderPropertyMapping Viewset
|
||||
parameters:
|
||||
- in: query
|
||||
name: expression
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: managed
|
||||
schema:
|
||||
@ -14544,6 +14552,10 @@ paths:
|
||||
type: string
|
||||
explode: true
|
||||
style: form
|
||||
- in: query
|
||||
name: managed__isnull
|
||||
schema:
|
||||
type: boolean
|
||||
- in: query
|
||||
name: name
|
||||
schema:
|
||||
@ -14566,11 +14578,6 @@ paths:
|
||||
description: Number of results to return per page.
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: pm_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- name: search
|
||||
required: false
|
||||
in: query
|
||||
@ -14818,14 +14825,6 @@ paths:
|
||||
operationId: propertymappings_saml_list
|
||||
description: SAMLPropertyMapping Viewset
|
||||
parameters:
|
||||
- in: query
|
||||
name: expression
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: friendly_name
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: managed
|
||||
schema:
|
||||
@ -14834,6 +14833,10 @@ paths:
|
||||
type: string
|
||||
explode: true
|
||||
style: form
|
||||
- in: query
|
||||
name: managed__isnull
|
||||
schema:
|
||||
type: boolean
|
||||
- in: query
|
||||
name: name
|
||||
schema:
|
||||
@ -14856,15 +14859,6 @@ paths:
|
||||
description: Number of results to return per page.
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: pm_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- in: query
|
||||
name: saml_name
|
||||
schema:
|
||||
type: string
|
||||
- name: search
|
||||
required: false
|
||||
in: query
|
||||
@ -15112,10 +15106,6 @@ paths:
|
||||
operationId: propertymappings_scim_list
|
||||
description: SCIMMapping Viewset
|
||||
parameters:
|
||||
- in: query
|
||||
name: expression
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: managed
|
||||
schema:
|
||||
@ -15124,6 +15114,10 @@ paths:
|
||||
type: string
|
||||
explode: true
|
||||
style: form
|
||||
- in: query
|
||||
name: managed__isnull
|
||||
schema:
|
||||
type: boolean
|
||||
- in: query
|
||||
name: name
|
||||
schema:
|
||||
@ -15146,11 +15140,6 @@ paths:
|
||||
description: Number of results to return per page.
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: pm_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- name: search
|
||||
required: false
|
||||
in: query
|
||||
@ -15406,6 +15395,10 @@ paths:
|
||||
type: string
|
||||
explode: true
|
||||
style: form
|
||||
- in: query
|
||||
name: managed__isnull
|
||||
schema:
|
||||
type: boolean
|
||||
- in: query
|
||||
name: name
|
||||
schema:
|
||||
@ -15679,10 +15672,6 @@ paths:
|
||||
operationId: propertymappings_source_ldap_list
|
||||
description: LDAP PropertyMapping Viewset
|
||||
parameters:
|
||||
- in: query
|
||||
name: expression
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: managed
|
||||
schema:
|
||||
@ -15691,6 +15680,10 @@ paths:
|
||||
type: string
|
||||
explode: true
|
||||
style: form
|
||||
- in: query
|
||||
name: managed__isnull
|
||||
schema:
|
||||
type: boolean
|
||||
- in: query
|
||||
name: name
|
||||
schema:
|
||||
@ -15713,11 +15706,6 @@ paths:
|
||||
description: Number of results to return per page.
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: pm_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- name: search
|
||||
required: false
|
||||
in: query
|
||||
|
Reference in New Issue
Block a user