*: rewrite managed objects, use nullable text flag instead of boolean as uid (#533)

This commit is contained in:
Jens L 2021-02-06 16:56:21 +01:00 committed by GitHub
parent 05d777c373
commit a6ac82c492
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 115 additions and 70 deletions

View File

@ -42,7 +42,7 @@ class PropertyMappingViewSet(ReadOnlyModelViewSet):
search_fields = [ search_fields = [
"name", "name",
] ]
filterset_fields = ["managed"] filterset_fields = {"managed": ["isnull"]}
ordering = ["name"] ordering = ["name"]
def get_queryset(self): def get_queryset(self):

View File

@ -13,19 +13,23 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="propertymapping", model_name="propertymapping",
name="managed", name="managed",
field=models.BooleanField( field=models.TextField(
default=False, default=None,
help_text="Objects which are managed by authentik. These objects are created and updated automatically. This is flag only indicates that an object can be overwritten by migrations. You can still modify the objects via the API, but expect changes to be overwritten in a later update.", help_text="Objects which are managed by authentik. These objects are created and updated automatically. This is flag only indicates that an object can be overwritten by migrations. You can still modify the objects via the API, but expect changes to be overwritten in a later update.",
null=True,
verbose_name="Managed by authentik", verbose_name="Managed by authentik",
unique=True,
), ),
), ),
migrations.AddField( migrations.AddField(
model_name="token", model_name="token",
name="managed", name="managed",
field=models.BooleanField( field=models.TextField(
default=False, default=None,
help_text="Objects which are managed by authentik. These objects are created and updated automatically. This is flag only indicates that an object can be overwritten by migrations. You can still modify the objects via the API, but expect changes to be overwritten in a later update.", help_text="Objects which are managed by authentik. These objects are created and updated automatically. This is flag only indicates that an object can be overwritten by migrations. You can still modify the objects via the API, but expect changes to be overwritten in a later update.",
null=True,
verbose_name="Managed by authentik", verbose_name="Managed by authentik",
unique=True,
), ),
), ),
] ]

View File

@ -12,12 +12,12 @@ class EnsureOp:
"""Ensure operation, executed as part of an ObjectManager run""" """Ensure operation, executed as part of an ObjectManager run"""
_obj: Type[ManagedModel] _obj: Type[ManagedModel]
_match_fields: tuple[str, ...] _managed_uid: str
_kwargs: dict _kwargs: dict
def __init__(self, obj: Type[ManagedModel], *match_fields: str, **kwargs) -> None: def __init__(self, obj: Type[ManagedModel], managed_uid: str, **kwargs) -> None:
self._obj = obj self._obj = obj
self._match_fields = match_fields self._managed_uid = managed_uid
self._kwargs = kwargs self._kwargs = kwargs
def run(self): def run(self):
@ -29,16 +29,13 @@ class EnsureExists(EnsureOp):
"""Ensure object exists, with kwargs as given values""" """Ensure object exists, with kwargs as given values"""
def run(self): def run(self):
update_kwargs = { self._kwargs.setdefault("managed", self._managed_uid)
"managed": True, self._obj.objects.update_or_create(
"defaults": self._kwargs, **{
} "managed": self._managed_uid,
for field in self._match_fields: "defaults": self._kwargs,
value = self._kwargs.get(field, None) }
if value: )
update_kwargs[field] = value
self._kwargs.setdefault("managed", True)
self._obj.objects.update_or_create(**update_kwargs)
class ObjectManager: class ObjectManager:

View File

@ -7,8 +7,9 @@ from django.utils.translation import gettext_lazy as _
class ManagedModel(models.Model): class ManagedModel(models.Model):
"""Model which can be managed by authentik exclusively""" """Model which can be managed by authentik exclusively"""
managed = models.BooleanField( managed = models.TextField(
default=False, default=None,
null=True,
verbose_name=_("Managed by authentik"), verbose_name=_("Managed by authentik"),
help_text=_( help_text=_(
( (
@ -18,11 +19,12 @@ class ManagedModel(models.Model):
"to be overwritten in a later update." "to be overwritten in a later update."
) )
), ),
unique=True,
) )
def managed_objects(self) -> QuerySet: def managed_objects(self) -> QuerySet:
"""Get all objects which are managed""" """Get all objects which are managed"""
return self.objects.filter(managed=True) return self.objects.exclude(managed__isnull=True)
class Meta: class Meta:

View File

@ -363,7 +363,7 @@ class Outpost(models.Model):
intent=TokenIntents.INTENT_API, intent=TokenIntents.INTENT_API,
description=f"Autogenerated by authentik for Outpost {self.name}", description=f"Autogenerated by authentik for Outpost {self.name}",
expiring=False, expiring=False,
managed=True, managed="goauthentik.io/outpost",
) )
def get_required_objects(self) -> Iterable[models.Model]: def get_required_objects(self) -> Iterable[models.Model]:

View File

@ -34,14 +34,14 @@ class ScopeMappingManager(ObjectManager):
return [ return [
EnsureExists( EnsureExists(
ScopeMapping, ScopeMapping,
"scope_name", "goauthentik.io/providers/oauth2/scope-openid",
name="authentik default OAuth Mapping: OpenID 'openid'", name="authentik default OAuth Mapping: OpenID 'openid'",
scope_name="openid", scope_name="openid",
expression=SCOPE_OPENID_EXPRESSION, expression=SCOPE_OPENID_EXPRESSION,
), ),
EnsureExists( EnsureExists(
ScopeMapping, ScopeMapping,
"scope_name", "goauthentik.io/providers/oauth2/scope-email",
name="authentik default OAuth Mapping: OpenID 'email'", name="authentik default OAuth Mapping: OpenID 'email'",
scope_name="email", scope_name="email",
description="Email address", description="Email address",
@ -49,7 +49,7 @@ class ScopeMappingManager(ObjectManager):
), ),
EnsureExists( EnsureExists(
ScopeMapping, ScopeMapping,
"scope_name", "goauthentik.io/providers/oauth2/scope-profile",
name="authentik default OAuth Mapping: OpenID 'profile'", name="authentik default OAuth Mapping: OpenID 'profile'",
scope_name="profile", scope_name="profile",
description="General Profile Information", description="General Profile Information",

View File

@ -3,6 +3,13 @@
from django.apps.registry import Apps from django.apps.registry import Apps
from django.db import migrations from django.db import migrations
scope_uid_map = {
"openid": "goauthentik.io/providers/oauth2/scope-openid",
"email": "goauthentik.io/providers/oauth2/scope-email",
"profile": "goauthentik.io/providers/oauth2/scope-profile",
"ak_proxy": "goauthentik.io/providers/proxy/scope-proxy",
}
def set_managed_flag(apps: Apps, schema_editor): def set_managed_flag(apps: Apps, schema_editor):
ScopeMapping = apps.get_model("authentik_providers_oauth2", "ScopeMapping") ScopeMapping = apps.get_model("authentik_providers_oauth2", "ScopeMapping")
@ -10,13 +17,14 @@ def set_managed_flag(apps: Apps, schema_editor):
for mapping in ScopeMapping.objects.using(db_alias).filter( for mapping in ScopeMapping.objects.using(db_alias).filter(
name__startswith="Autogenerated " name__startswith="Autogenerated "
): ):
mapping.managed = True mapping.managed = scope_uid_map[mapping.scope_name]
mapping.save() mapping.save()
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("authentik_core", "0017_managed"),
("authentik_providers_oauth2", "0010_auto_20201227_1804"), ("authentik_providers_oauth2", "0010_auto_20201227_1804"),
] ]

View File

@ -20,7 +20,7 @@ class ProxyScopeMappingManager(ObjectManager):
return [ return [
EnsureExists( EnsureExists(
ScopeMapping, ScopeMapping,
"scope_name", "goauthentik.io/providers/proxy/scope-proxy",
name="authentik default OAuth Mapping: proxy outpost", name="authentik default OAuth Mapping: proxy outpost",
scope_name=SCOPE_AK_PROXY, scope_name=SCOPE_AK_PROXY,
expression=SCOPE_AK_PROXY_EXPRESSION, expression=SCOPE_AK_PROXY_EXPRESSION,

View File

@ -2,6 +2,11 @@
from authentik.managed.manager import EnsureExists, ObjectManager from authentik.managed.manager import EnsureExists, ObjectManager
from authentik.providers.saml.models import SAMLPropertyMapping from authentik.providers.saml.models import SAMLPropertyMapping
GROUP_EXPRESSION = """
for group in user.ak_groups.all():
yield group.name
"""
class SAMLProviderManager(ObjectManager): class SAMLProviderManager(ObjectManager):
"""SAML Provider managed objects""" """SAML Provider managed objects"""
@ -10,53 +15,53 @@ class SAMLProviderManager(ObjectManager):
return [ return [
EnsureExists( EnsureExists(
SAMLPropertyMapping, SAMLPropertyMapping,
"saml_name", "goauthentik.io/providers/saml/upn",
name="authentik default SAML Mapping: UPN", name="authentik default SAML Mapping: UPN",
saml_name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn", saml_name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn",
expression="return user.attributes.get('upn', user.email)", expression="return user.attributes.get('upn', user.email)",
), ),
EnsureExists( EnsureExists(
SAMLPropertyMapping, SAMLPropertyMapping,
"saml_name", "goauthentik.io/providers/saml/name",
name="authentik default SAML Mapping: Name", name="authentik default SAML Mapping: Name",
saml_name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", saml_name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
expression="return user.name", expression="return user.name",
), ),
EnsureExists( EnsureExists(
SAMLPropertyMapping, SAMLPropertyMapping,
"saml_name", "goauthentik.io/providers/saml/email",
name="authentik default SAML Mapping: Email", name="authentik default SAML Mapping: Email",
saml_name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", saml_name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
expression="return user.email", expression="return user.email",
), ),
EnsureExists( EnsureExists(
SAMLPropertyMapping, SAMLPropertyMapping,
"saml_name", "goauthentik.io/providers/saml/username",
name="authentik default SAML Mapping: Username", name="authentik default SAML Mapping: Username",
saml_name="http://schemas.goauthentik.io/2021/02/saml/username", saml_name="http://schemas.goauthentik.io/2021/02/saml/username",
expression="return user.username", expression="return user.username",
), ),
EnsureExists( EnsureExists(
SAMLPropertyMapping, SAMLPropertyMapping,
"saml_name", "goauthentik.io/providers/saml/uid",
name="authentik default SAML Mapping: User ID", name="authentik default SAML Mapping: User ID",
saml_name="http://schemas.goauthentik.io/2021/02/saml/uid", saml_name="http://schemas.goauthentik.io/2021/02/saml/uid",
expression="return user.pk", expression="return user.pk",
), ),
EnsureExists( EnsureExists(
SAMLPropertyMapping, SAMLPropertyMapping,
"saml_name", "goauthentik.io/providers/saml/groups",
name="authentik default SAML Mapping: Groups",
saml_name="http://schemas.xmlsoap.org/claims/Group",
expression=GROUP_EXPRESSION,
),
EnsureExists(
SAMLPropertyMapping,
"goauthentik.io/providers/saml/ms-windowsaccountname",
name="authentik default SAML Mapping: WindowsAccountname (Username)", name="authentik default SAML Mapping: WindowsAccountname (Username)",
saml_name=( saml_name=(
"http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname" "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"
), ),
expression="return user.username", expression="return user.username",
), ),
EnsureExists(
SAMLPropertyMapping,
"saml_name",
name="authentik default SAML Mapping: Groups",
saml_name="http://schemas.xmlsoap.org/claims/Group",
expression="for group in user.ak_groups.all():\n yield group.name",
),
] ]

View File

@ -6,6 +6,7 @@ saml_name_map = {
"http://schemas.xmlsoap.org/claims/CommonName": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", "http://schemas.xmlsoap.org/claims/CommonName": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname": "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname": "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname",
"member-of": "http://schemas.xmlsoap.org/claims/Group", "member-of": "http://schemas.xmlsoap.org/claims/Group",
"http://schemas.xmlsoap.org/claims/Group": "http://schemas.xmlsoap.org/claims/Group",
"urn:oid:0.9.2342.19200300.100.1.1": "http://schemas.goauthentik.io/2021/02/saml/uid", "urn:oid:0.9.2342.19200300.100.1.1": "http://schemas.goauthentik.io/2021/02/saml/uid",
"urn:oid:0.9.2342.19200300.100.1.3": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", "urn:oid:0.9.2342.19200300.100.1.3": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
"urn:oid:1.3.6.1.4.1.5923.1.1.1.6": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn", "urn:oid:1.3.6.1.4.1.5923.1.1.1.6": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn",
@ -13,6 +14,16 @@ saml_name_map = {
"urn:oid:2.5.4.3": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", "urn:oid:2.5.4.3": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
} }
saml_name_uid_map = {
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn": "goauthentik.io/providers/saml/upn",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "goauthentik.io/providers/saml/name",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": "goauthentik.io/providers/saml/email",
"http://schemas.goauthentik.io/2021/02/saml/username": "goauthentik.io/providers/saml/username",
"http://schemas.goauthentik.io/2021/02/saml/uid": "goauthentik.io/providers/saml/uid",
"http://schemas.xmlsoap.org/claims/Group": "goauthentik.io/providers/saml/groups",
"http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname": "goauthentik.io/providers/saml/ms-windowsaccountname",
}
def add_managed_update(apps, schema_editor): def add_managed_update(apps, schema_editor):
"""Create default SAML Property Mappings""" """Create default SAML Property Mappings"""
@ -23,15 +34,14 @@ def add_managed_update(apps, schema_editor):
for pm in SAMLPropertyMapping.objects.using(db_alias).filter( for pm in SAMLPropertyMapping.objects.using(db_alias).filter(
name__startswith="Autogenerated " name__startswith="Autogenerated "
): ):
pm.managed = True
if pm.saml_name not in saml_name_map: if pm.saml_name not in saml_name_map:
pm.save()
continue continue
new_name = saml_name_map[pm.saml_name] new_name = saml_name_map[pm.saml_name]
if not new_name: if not new_name:
pm.delete() pm.delete()
continue continue
pm.saml_name = new_name pm.saml_name = new_name
pm.managed = saml_name_uid_map[new_name]
pm.save() pm.save()

View File

@ -10,15 +10,14 @@ class LDAPProviderManager(ObjectManager):
return [ return [
EnsureExists( EnsureExists(
LDAPPropertyMapping, LDAPPropertyMapping,
"object_field", "goauthentik.io/sources/ldap/default-name",
"expression", name="authentik default LDAP Mapping: Name",
name="authentik default LDAP Mapping: name",
object_field="name", object_field="name",
expression="return ldap.get('name')", expression="return ldap.get('name')",
), ),
EnsureExists( EnsureExists(
LDAPPropertyMapping, LDAPPropertyMapping,
"object_field", "goauthentik.io/sources/ldap/default-mail",
name="authentik default LDAP Mapping: mail", name="authentik default LDAP Mapping: mail",
object_field="email", object_field="email",
expression="return ldap.get('mail')", expression="return ldap.get('mail')",
@ -26,32 +25,43 @@ class LDAPProviderManager(ObjectManager):
# Active Directory-specific mappings # Active Directory-specific mappings
EnsureExists( EnsureExists(
LDAPPropertyMapping, LDAPPropertyMapping,
"object_field", "goauthentik.io/sources/ldap/ms-samaccountname",
"expression",
name="authentik default Active Directory Mapping: sAMAccountName", name="authentik default Active Directory Mapping: sAMAccountName",
object_field="username", object_field="username",
expression="return ldap.get('sAMAccountName')", expression="return ldap.get('sAMAccountName')",
), ),
EnsureExists( EnsureExists(
LDAPPropertyMapping, LDAPPropertyMapping,
"object_field", "goauthentik.io/sources/ldap/ms-userprincipalname",
name="authentik default Active Directory Mapping: userPrincipalName", name="authentik default Active Directory Mapping: userPrincipalName",
object_field="attributes.upn", object_field="attributes.upn",
expression="return ldap.get('userPrincipalName')", expression="return ldap.get('userPrincipalName')",
), ),
EnsureExists(
LDAPPropertyMapping,
"goauthentik.io/sources/ldap/ms-givenName",
name="authentik default Active Directory Mapping: givenName",
object_field="attributes.givenName",
expression="return ldap.get('givenName')",
),
EnsureExists(
LDAPPropertyMapping,
"goauthentik.io/sources/ldap/ms-sn",
name="authentik default Active Directory Mapping: sn",
object_field="attributes.sn",
expression="return ldap.get('sn')",
),
# OpenLDAP specific mappings # OpenLDAP specific mappings
EnsureExists( EnsureExists(
LDAPPropertyMapping, LDAPPropertyMapping,
"object_field", "goauthentik.io/sources/ldap/openldap-uid",
"expression",
name="authentik default OpenLDAP Mapping: uid", name="authentik default OpenLDAP Mapping: uid",
object_field="username", object_field="username",
expression="return ldap.get('uid')", expression="return ldap.get('uid')",
), ),
EnsureExists( EnsureExists(
LDAPPropertyMapping, LDAPPropertyMapping,
"object_field", "goauthentik.io/sources/ldap/openldap-cn",
"expression",
name="authentik default OpenLDAP Mapping: cn", name="authentik default OpenLDAP Mapping: cn",
object_field="name", object_field="name",
expression="return ldap.get('cn')", expression="return ldap.get('cn')",

View File

@ -9,16 +9,25 @@ def set_managed_flag(apps: Apps, schema_editor):
"authentik_sources_ldap", "LDAPPropertyMapping" "authentik_sources_ldap", "LDAPPropertyMapping"
) )
db_alias = schema_editor.connection.alias db_alias = schema_editor.connection.alias
field_to_uid = {
"name": "goauthentik.io/sources/ldap/default-name",
"email": "goauthentik.io/sources/ldap/default-mail",
"username": "goauthentik.io/sources/ldap/ms-samaccountname",
"attributes.upn": "goauthentik.io/sources/ldap/ms-userprincipalname",
"first_name": "goauthentik.io/sources/ldap/ms-givenName",
"last_name": "goauthentik.io/sources/ldap/ms-sn",
}
for mapping in LDAPPropertyMapping.objects.using(db_alias).filter( for mapping in LDAPPropertyMapping.objects.using(db_alias).filter(
name__startswith="Autogenerated " name__startswith="Autogenerated "
): ):
mapping.managed = True mapping.managed = field_to_uid.get(mapping.object_field)
mapping.save() mapping.save()
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("authentik_core", "0017_managed"),
("authentik_sources_ldap", "0007_ldapsource_sync_users_password"), ("authentik_sources_ldap", "0007_ldapsource_sync_users_password"),
] ]

View File

@ -35,8 +35,8 @@ class LDAPSyncTests(TestCase):
"""Test user sync""" """Test user sync"""
self.source.property_mappings.set( self.source.property_mappings.set(
LDAPPropertyMapping.objects.filter( LDAPPropertyMapping.objects.filter(
Q(name__startswith="authentik default LDAP Mapping") Q(managed__startswith="goauthentik.io/sources/ldap/default")
| Q(name__startswith="authentik default Active Directory Mapping") | Q(managed__startswith="goauthentik.io/sources/ldap/ms")
) )
) )
self.source.save() self.source.save()
@ -52,8 +52,8 @@ class LDAPSyncTests(TestCase):
self.source.object_uniqueness_field = "uid" self.source.object_uniqueness_field = "uid"
self.source.property_mappings.set( self.source.property_mappings.set(
LDAPPropertyMapping.objects.filter( LDAPPropertyMapping.objects.filter(
Q(name__startswith="authentik default LDAP Mapping") Q(managed__startswith="goauthentik.io/sources/ldap/default")
| Q(name__startswith="authentik default OpenLDAP Mapping") | Q(managed__startswith="goauthentik.io/sources/ldap/openldap")
) )
) )
self.source.save() self.source.save()
@ -68,13 +68,13 @@ class LDAPSyncTests(TestCase):
"""Test group sync""" """Test group sync"""
self.source.property_mappings.set( self.source.property_mappings.set(
LDAPPropertyMapping.objects.filter( LDAPPropertyMapping.objects.filter(
Q(name__startswith="authentik default LDAP Mapping") Q(managed__startswith="goauthentik.io/sources/ldap/default")
| Q(name__startswith="authentik default Active Directory Mapping") | Q(managed__startswith="goauthentik.io/sources/ldap/ms")
) )
) )
self.source.property_mappings_group.set( self.source.property_mappings_group.set(
LDAPPropertyMapping.objects.filter( LDAPPropertyMapping.objects.filter(
name="authentik default LDAP Mapping: name" managed="goauthentik.io/sources/ldap/default-name"
) )
) )
self.source.save() self.source.save()
@ -93,13 +93,13 @@ class LDAPSyncTests(TestCase):
self.source.group_object_filter = "(objectClass=groupOfNames)" self.source.group_object_filter = "(objectClass=groupOfNames)"
self.source.property_mappings.set( self.source.property_mappings.set(
LDAPPropertyMapping.objects.filter( LDAPPropertyMapping.objects.filter(
Q(name__startswith="authentik default LDAP Mapping") Q(managed__startswith="goauthentik.io/sources/ldap/default")
| Q(name__startswith="authentik default OpenLDAP Mapping") | Q(managed__startswith="goauthentik.io/sources/ldap/openldap")
) )
) )
self.source.property_mappings_group.set( self.source.property_mappings_group.set(
LDAPPropertyMapping.objects.filter( LDAPPropertyMapping.objects.filter(
name="authentik default OpenLDAP Mapping: cn" managed="goauthentik.io/sources/ldap/openldap-cn"
) )
) )
self.source.save() self.source.save()
@ -116,8 +116,8 @@ class LDAPSyncTests(TestCase):
"""Test Scheduled tasks""" """Test Scheduled tasks"""
self.source.property_mappings.set( self.source.property_mappings.set(
LDAPPropertyMapping.objects.filter( LDAPPropertyMapping.objects.filter(
Q(name__startswith="authentik default LDAP Mapping") Q(managed__startswith="goauthentik.io/sources/ldap/default")
| Q(name__startswith="authentik default Active Directory Mapping") | Q(managed__startswith="goauthentik.io/sources/ldap/ms")
) )
) )
self.source.save() self.source.save()
@ -131,8 +131,8 @@ class LDAPSyncTests(TestCase):
self.source.group_object_filter = "(objectClass=groupOfNames)" self.source.group_object_filter = "(objectClass=groupOfNames)"
self.source.property_mappings.set( self.source.property_mappings.set(
LDAPPropertyMapping.objects.filter( LDAPPropertyMapping.objects.filter(
Q(name__startswith="authentik default LDAP Mapping") Q(managed__startswith="goauthentik.io/sources/ldap/default")
| Q(name__startswith="authentik default OpenLDAP Mapping") | Q(managed__startswith="goauthentik.io/sources/ldap/openldap")
) )
) )
self.source.save() self.source.save()

View File

@ -3597,7 +3597,7 @@ paths:
operationId: propertymappings_all_list operationId: propertymappings_all_list
description: PropertyMapping Viewset description: PropertyMapping Viewset
parameters: parameters:
- name: managed - name: managed__isnull
in: query in: query
description: '' description: ''
required: false required: false

View File

@ -35,7 +35,7 @@ export class PropertyMappingListPage extends TablePage<PropertyMapping> {
ordering: this.order, ordering: this.order,
page: page, page: page,
search: this.search || "", search: this.search || "",
managed: this.hideManaged ? false : null, managed__isnull: this.hideManaged,
}); });
} }