141 lines
4.2 KiB
Python
141 lines
4.2 KiB
Python
# -*- encoding: utf-8 -*-
|
|
|
|
from bonsai import LDAPEntry
|
|
from multidict import MultiDict
|
|
|
|
from phi.async_ldap import mixins
|
|
from phi.exceptions import (
|
|
PhiUnexpectedRuntimeValue,
|
|
)
|
|
|
|
|
|
def parse_dn(dn):
|
|
return MultiDict(e.split("=") for e in dn.split(","))
|
|
|
|
|
|
def get_dn(obj):
|
|
if isinstance(obj, mixins.Entry):
|
|
return obj.dn
|
|
elif isinstance(obj, LDAPEntry):
|
|
return obj["dn"]
|
|
elif isinstance(obj, str):
|
|
return obj
|
|
else:
|
|
raise ValueError(f"Unacceptable input: {obj}")
|
|
|
|
|
|
class User(mixins.Member, mixins.Entry, mixins.Singleton):
|
|
object_class = [
|
|
"inetOrgPerson",
|
|
"simpleSecurityObject",
|
|
"organizationalPerson",
|
|
"person",
|
|
"top",
|
|
]
|
|
_instances = dict() # type: ignore
|
|
id_tag = "uid"
|
|
ou = "Hackers"
|
|
ldap_attributes = ["uid", "ou", "cn", "sn", "mail", "userPassword"]
|
|
|
|
async def iter_groups(self): # To be monkeypatched later
|
|
pass # pragma: no cover
|
|
|
|
async def groups(self):
|
|
return [g async for g in self.iter_groups()]
|
|
|
|
async def delete(self):
|
|
async for group in self.iter_groups():
|
|
await group.remove_member(self)
|
|
await super().delete()
|
|
|
|
|
|
class Hackers(mixins.OrganizationalUnit, mixins.Entry, mixins.Singleton):
|
|
_instances = dict() # type: ignore
|
|
ou = "Hackers"
|
|
child_class = User
|
|
|
|
|
|
class Service(mixins.Member, mixins.Entry, mixins.Singleton):
|
|
object_class = ["simpleSecurityObject", "account", "top"]
|
|
_instances = dict() # type: ignore
|
|
id_tag = "uid"
|
|
ou = "Robots"
|
|
ldap_attributes = ["uid", "ou", "userPassword"]
|
|
|
|
async def iter_groups(self): # To be monkeypatched later
|
|
raise NotImplemented
|
|
|
|
async def groups(self):
|
|
return [g async for g in self.iter_groups()]
|
|
|
|
async def delete(self):
|
|
async for group in self.iter_groups():
|
|
await group.remove_member(self)
|
|
await super().delete()
|
|
|
|
|
|
class Robots(mixins.OrganizationalUnit, mixins.Entry, mixins.Singleton):
|
|
_instances = dict() # type: ignore
|
|
ou = "Robots"
|
|
child_class = Service
|
|
|
|
|
|
class Group(mixins.Member, mixins.Entry, mixins.Singleton):
|
|
object_class = ["groupOfNames", "top"]
|
|
_instances = dict() # type: ignore
|
|
id_tag = "cn"
|
|
ou = "Congregations"
|
|
ldap_attributes = ["cn", "ou", "member"]
|
|
memeber_classes = {"Hackers": User, "Robots": Service}
|
|
empty = False
|
|
|
|
async def add_member(self, member):
|
|
member_dn = get_dn(member)
|
|
self._entry["member"].append(member_dn)
|
|
await self.modify()
|
|
|
|
async def remove_member(self, member):
|
|
new_group_members = [get_dn(m) async for m in self.get_members() if member != m]
|
|
if len(new_group_members) == 0:
|
|
await self.delete()
|
|
self.empty = True
|
|
else:
|
|
self._entry["member"] = new_group_members
|
|
await self.modify()
|
|
|
|
async def get_members(self):
|
|
await self.sync()
|
|
for member in self._entry.get("member", []):
|
|
dn = parse_dn(member)
|
|
yield self.memeber_classes.get(dn["ou"])(self.client, dn["uid"])
|
|
|
|
|
|
class Congregations(mixins.OrganizationalUnit, mixins.Entry, mixins.Singleton):
|
|
_instances = dict() # type: ignore
|
|
ou = "Congregations"
|
|
child_class = Group
|
|
|
|
|
|
# We define this async method here **after** `User`, `Service` and `Group` have been
|
|
# defined, in order to avoid definition loops that would prevent the code from running.
|
|
# Indeed, this function explicitely uses `Group` but is needed as a `User` and `Service`
|
|
# method. In turn, `Group` definition relies on both `User` and `Service` being yet
|
|
# defined.
|
|
async def iter_groups(self):
|
|
async with self.client.connect(is_async=True) as conn:
|
|
res = await conn.search(f"{self.dn}", 2, attrlist=["memberOf"])
|
|
if not res or len(res) == 0:
|
|
return
|
|
elif len(res) == 1:
|
|
for group in res[0].get("memberOf", []):
|
|
yield Group(self.client, parse_dn(get_dn(group))["cn"])
|
|
else:
|
|
raise PhiUnexpectedRuntimeValue(
|
|
"return value should be no more than one", res
|
|
)
|
|
|
|
|
|
# Monkeypatch
|
|
User.iter_groups = iter_groups # type: ignore
|
|
Service.iter_groups = iter_groups # type: ignore
|