This repository has been archived on 2024-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
authentik/passbook/sources/ldap/models.py

133 lines
4.2 KiB
Python
Raw Normal View History

2018-11-26 16:18:56 +00:00
"""passbook LDAP Models"""
from datetime import datetime
from typing import Optional, Type
2018-11-11 12:41:48 +00:00
from django.core.cache import cache
2018-11-26 16:18:56 +00:00
from django.db import models
from django.forms import ModelForm
from django.utils.translation import gettext_lazy as _
from ldap3 import ALL, Connection, Server
2018-11-11 12:41:48 +00:00
2019-10-10 15:36:09 +00:00
from passbook.core.models import Group, PropertyMapping, Source
from passbook.lib.models import DomainlessURLValidator
from passbook.lib.utils.template import render_to_string
2018-11-26 16:18:56 +00:00
class LDAPSource(Source):
2020-07-01 16:40:52 +00:00
"""Federate LDAP Directory with passbook, or create new accounts in LDAP."""
2018-11-26 16:18:56 +00:00
server_uri = models.TextField(
validators=[DomainlessURLValidator(schemes=["ldap", "ldaps"])],
verbose_name=_("Server URI"),
)
bind_cn = models.TextField(verbose_name=_("Bind CN"))
2018-11-26 16:18:56 +00:00
bind_password = models.TextField()
start_tls = models.BooleanField(default=False, verbose_name=_("Enable Start TLS"))
2018-11-26 16:18:56 +00:00
base_dn = models.TextField(verbose_name=_("Base DN"))
2019-12-31 11:51:16 +00:00
additional_user_dn = models.TextField(
help_text=_("Prepended to Base DN for User-queries."),
verbose_name=_("Addition User DN"),
blank=True,
2019-12-31 11:51:16 +00:00
)
additional_group_dn = models.TextField(
help_text=_("Prepended to Base DN for Group-queries."),
verbose_name=_("Addition Group DN"),
blank=True,
2019-12-31 11:51:16 +00:00
)
user_object_filter = models.TextField(
default="(objectCategory=Person)",
help_text=_("Consider Objects matching this filter to be Users."),
)
user_group_membership_field = models.TextField(
default="memberOf", help_text=_("Field which contains Groups of user.")
)
group_object_filter = models.TextField(
default="(objectCategory=Group)",
help_text=_("Consider Objects matching this filter to be Groups."),
)
object_uniqueness_field = models.TextField(
default="objectSid", help_text=_("Field which contains a unique Identifier.")
)
2019-10-10 15:36:09 +00:00
sync_users = models.BooleanField(default=True)
sync_users_password = models.BooleanField(
default=True,
help_text=_(
(
"When a user changes their password, sync it back to LDAP. "
"This can only be enabled on a single LDAP source."
)
),
unique=True,
)
2019-10-10 15:36:09 +00:00
sync_groups = models.BooleanField(default=True)
2019-12-31 11:51:16 +00:00
sync_parent_group = models.ForeignKey(
Group, blank=True, null=True, default=None, on_delete=models.SET_DEFAULT
)
2018-11-26 16:18:56 +00:00
@property
def form(self) -> Type[ModelForm]:
from passbook.sources.ldap.forms import LDAPSourceForm
return LDAPSourceForm
2018-11-26 17:22:38 +00:00
def state_cache_prefix(self, suffix: str) -> str:
"""Key by which the ldap source status is saved"""
return f"source_ldap_{self.pk}_state_{suffix}"
@property
def ui_additional_info(self) -> str:
last_sync = cache.get(self.state_cache_prefix("last_sync"), None)
if last_sync:
last_sync = datetime.fromtimestamp(last_sync)
return render_to_string(
"ldap/source_list_status.html", {"source": self, "last_sync": last_sync}
)
_connection: Optional[Connection] = None
@property
def connection(self) -> Connection:
"""Get a fully connected and bound LDAP Connection"""
if not self._connection:
server = Server(self.server_uri, get_info=ALL)
self._connection = Connection(
server,
raise_exceptions=True,
user=self.bind_cn,
password=self.bind_password,
)
self._connection.bind()
if self.start_tls:
self._connection.start_tls()
return self._connection
2018-11-26 16:18:56 +00:00
class Meta:
2019-12-31 11:51:16 +00:00
verbose_name = _("LDAP Source")
verbose_name_plural = _("LDAP Sources")
2018-11-11 12:41:48 +00:00
2019-10-10 15:36:09 +00:00
class LDAPPropertyMapping(PropertyMapping):
2020-07-01 16:40:52 +00:00
"""Map LDAP Property to User or Group object attribute"""
2018-11-11 12:41:48 +00:00
2019-10-10 15:36:09 +00:00
object_field = models.TextField()
@property
def form(self) -> Type[ModelForm]:
from passbook.sources.ldap.forms import LDAPPropertyMappingForm
return LDAPPropertyMappingForm
def __str__(self):
return self.name
class Meta:
2019-12-31 11:51:16 +00:00
verbose_name = _("LDAP Property Mapping")
verbose_name_plural = _("LDAP Property Mappings")