Switch to mixins to build async_ldap model classes
This commit is contained in:
parent
205c87dc49
commit
67c83975d1
136
src/phi/async_ldap/mixins.py
Normal file
136
src/phi/async_ldap/mixins.py
Normal file
|
@ -0,0 +1,136 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
from bonsai import LDAPEntry, LDAPModOp, NoSuchObjectError # type: ignore
|
||||
|
||||
from phi.exceptions import (
|
||||
PhiAttributeMissing,
|
||||
PhiEntryDoesNotExist,
|
||||
PhiEntryExists,
|
||||
PhiUnauthorized,
|
||||
PhiUnexpectedRuntimeValue,
|
||||
)
|
||||
|
||||
ATTR_TAG = "_ldap_"
|
||||
ATTR_TAG_LEN = len(ATTR_TAG)
|
||||
|
||||
|
||||
async def build_heritage(obj, child_class, attribute_id="uid"):
|
||||
"""
|
||||
Given the object and the child class, yields the
|
||||
instances of the children.
|
||||
"""
|
||||
async for child in obj.get_children():
|
||||
if attribute_id in child:
|
||||
_name = child[attribute_id][0]
|
||||
yield child_class(_name, obj.client)
|
||||
|
||||
|
||||
class Singleton(object):
|
||||
"""
|
||||
to singletonize a class. The class is crafted to be used with the mixins
|
||||
that implement the compatible __init__.
|
||||
"""
|
||||
|
||||
def __new__(cls, client, *args):
|
||||
if "name" in args:
|
||||
name = f"{cls.__name__}-{args['name']}-{id(client)}"
|
||||
else:
|
||||
name = f"{cls.__name__}-{id(client)}"
|
||||
if name not in cls._instances:
|
||||
cls._instances[name] = object.__new__(cls)
|
||||
return cls._instances[name]
|
||||
|
||||
|
||||
class Entry(object):
|
||||
"""
|
||||
to interact with LDAP.
|
||||
"""
|
||||
|
||||
def get_all_ldap_attributes(self):
|
||||
return [attr for attr in dir(self) if attr.startswith(ATTR_TAG)]
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.__class__.__name__} {self.dn}>"
|
||||
|
||||
def __str__(self):
|
||||
return f"<{self.__class__.__name__} {self.dn}>"
|
||||
|
||||
def __iter__(self):
|
||||
for attr in self.get_all_ldap_attributes():
|
||||
if not isinstance(getattr(type(self), attr), property) and callable(
|
||||
getattr(type(self), attr)
|
||||
):
|
||||
yield attr[ATTR_TAG_LEN:], getattr(self, attr)()
|
||||
else:
|
||||
yield attr[ATTR_TAG_LEN:], getattr(self, attr)
|
||||
|
||||
def __dict__(self):
|
||||
return dict(self)
|
||||
|
||||
async def _create_new(self):
|
||||
entry = LDAPEntry(self.dn)
|
||||
entry["objectClass"] = self.object_class
|
||||
entry.update(self.get_all_ldap_attributes())
|
||||
async with self.client.connect(is_async=True) as conn:
|
||||
await conn.add(entry)
|
||||
return entry
|
||||
|
||||
async def _get(self):
|
||||
async with self.client.connect(is_async=True) as conn:
|
||||
# This returns a list of dicts. It should always contain only one item:
|
||||
# the one we are interested.
|
||||
_res = await conn.search(self.dn, 0)
|
||||
if len(_res) == 0:
|
||||
return
|
||||
elif len(_res) > 1:
|
||||
raise PhiUnexpectedRuntimeValue(
|
||||
"return value should be no more than one", res
|
||||
)
|
||||
return _res[0]
|
||||
|
||||
async def describe(self):
|
||||
res = dict(await self._get())
|
||||
res["dn"] = self.dn
|
||||
res["name"] = self.name
|
||||
return res
|
||||
|
||||
|
||||
class OrganizationalUnit(object):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.base_dn = client.base_dn
|
||||
self.name = self.__class__.__name__
|
||||
self.children = build_heritage(self, getattr(self, "child_class"))
|
||||
self._entry = LDAPEntry(self.dn)
|
||||
|
||||
def __aiter__(self):
|
||||
return self
|
||||
|
||||
async def __anext__(self):
|
||||
try:
|
||||
return await self.children.__anext__()
|
||||
except StopAsyncIteration:
|
||||
self.children = build_heritage(self, getattr(self, "child_class"))
|
||||
raise
|
||||
|
||||
async def get_children(self):
|
||||
async with self.client.connect(is_async=True) as conn:
|
||||
for el in await conn.search(self.dn, 2):
|
||||
yield el
|
||||
|
||||
@property
|
||||
def dn(self):
|
||||
return f"ou={self.ou_name},{self.base_dn}"
|
||||
|
||||
|
||||
class Child(object):
|
||||
object_class = ["inetOrgPerson", "organizationalPerson", "person", "top"]
|
||||
|
||||
def __init__(self, client, name):
|
||||
self.client = client
|
||||
self.base_dn = client.base_dn
|
||||
self.name = name
|
||||
self._entry = LDAPEntry(self.dn)
|
||||
|
||||
@property
|
||||
def dn(self):
|
||||
return f"{self.id_tag}={self.name},ou={self.ou_name},{self.base_dn}"
|
15
src/phi/async_ldap/new_model.py
Normal file
15
src/phi/async_ldap/new_model.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from phi.async_ldap import mixins
|
||||
|
||||
|
||||
class User(mixins.Singleton, mixins.Entry, mixins.Child):
|
||||
_instances = dict()
|
||||
id_tag = "uid"
|
||||
ou_name = "Hackers"
|
||||
|
||||
|
||||
class Hackers(mixins.Singleton, mixins.Entry, mixins.OrganizationalUnit):
|
||||
_instances = dict()
|
||||
ou_name = "Hackers"
|
||||
child_class = User
|
Loading…
Reference in New Issue
Block a user