Massive code update
This commit is contained in:
parent
d024232d66
commit
ac0ab02d6e
|
@ -30,7 +30,6 @@ from phi.async_ldap.model import (
|
|||
)
|
||||
from phi.security import hash_pass
|
||||
|
||||
|
||||
BASE_DN = "dc=test,dc=abbiamoundominio,dc=org"
|
||||
USER_LIST = [{"uid": ["conte_mascetti"]}, {"uid": ["perozzi"]}, {"uid": ["necchi"]}]
|
||||
SERVICE_LIST = [{"uid": ["phi"]}, {"uid": ["irc"]}]
|
||||
|
|
96
async_tests/test_async_ldap_new_model.py
Normal file
96
async_tests/test_async_ldap_new_model.py
Normal file
|
@ -0,0 +1,96 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
import asyncio
|
||||
|
||||
from async_generator import asynccontextmanager
|
||||
from bonsai import LDAPEntry
|
||||
import mock
|
||||
import pytest
|
||||
|
||||
from phi.async_ldap.new_model import get_dn, User
|
||||
|
||||
BASE_DN = "dc=test,dc=domain,dc=tld"
|
||||
|
||||
|
||||
class MockClient(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.return_value = kwargs.get("return_value")
|
||||
self.connect_called = False
|
||||
self.conn = mock.MagicMock()
|
||||
|
||||
def connect_called_with_search(self):
|
||||
self.conn.search.assert_called()
|
||||
|
||||
def connect_called_with_add(self):
|
||||
self.conn.add.assert_called()
|
||||
|
||||
def connect_called_with_modify(self):
|
||||
self.conn.modify.assert_called()
|
||||
|
||||
def connect_called_with_delete(self):
|
||||
self.conn.delete.assert_called()
|
||||
|
||||
@property
|
||||
def base_dn(self):
|
||||
return BASE_DN
|
||||
|
||||
@asynccontextmanager
|
||||
async def connect(self, *args, **kwargs):
|
||||
self.connect_called = True
|
||||
|
||||
async def _search(*a, **kw):
|
||||
return self.return_value
|
||||
|
||||
async def _add(*a, **kw):
|
||||
return self.return_value
|
||||
|
||||
async def _modify(*a, **kw):
|
||||
return self.return_value
|
||||
|
||||
async def _delete(*a, **kw):
|
||||
return self.return_value
|
||||
|
||||
self.conn.search = mock.MagicMock(side_effect=_search)
|
||||
self.conn.add = mock.MagicMock(side_effect=_add)
|
||||
self.conn.modify = mock.MagicMock(side_effect=_modify)
|
||||
self.conn.delete = mock.MagicMock(side_effect=_delete)
|
||||
|
||||
yield self.conn
|
||||
|
||||
|
||||
cl = mock.MagicMock()
|
||||
cl.base_dn = BASE_DN
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"input_obj, expected_result",
|
||||
[
|
||||
(User(cl, "test_user"), f"uid=test_user,ou=Hackers,{BASE_DN}"),
|
||||
(
|
||||
LDAPEntry(f"uid=test_user,ou=Hackers,{BASE_DN}"),
|
||||
f"uid=test_user,ou=Hackers,{BASE_DN}",
|
||||
),
|
||||
(f"uid=test_user,ou=Hackers,{BASE_DN}", f"uid=test_user,ou=Hackers,{BASE_DN}"),
|
||||
],
|
||||
)
|
||||
def test_get_dn(input_obj, expected_result):
|
||||
assert get_dn(input_obj) == expected_result
|
||||
|
||||
|
||||
def test_get_dn_raises():
|
||||
with pytest.raises(ValueError) as e:
|
||||
_ = get_dn(object)
|
||||
|
||||
assert "Unacceptable input:" in str(e.value)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_User_add():
|
||||
_cl = MockClient(return_value=None)
|
||||
u = User(_cl, "test_user")
|
||||
|
||||
assert u.dn == f"uid=test_user,ou=Hackers,{BASE_DN}"
|
||||
|
||||
_ = await u.save()
|
||||
|
||||
assert _cl.connect_called
|
||||
_cl.connect_called_with_add()
|
|
@ -11,31 +11,31 @@ objectClass: organizationalUnit
|
|||
objectClass: top
|
||||
ou: Hackers
|
||||
|
||||
dn: ou=Services,dc=unit,dc=macaomilano,dc=org
|
||||
dn: ou=Robots,dc=unit,dc=macaomilano,dc=org
|
||||
objectClass: top
|
||||
objectClass: organizationalUnit
|
||||
ou: Services
|
||||
ou: Robots
|
||||
|
||||
dn: ou=Groups,dc=unit,dc=macaomilano,dc=org
|
||||
dn: ou=Congregations,dc=unit,dc=macaomilano,dc=org
|
||||
objectClass: top
|
||||
objectClass: organizationalUnit
|
||||
ou: Groups
|
||||
ou: Congregations
|
||||
|
||||
dn: uid=phi,ou=Services,dc=unit,dc=macaomilano,dc=org
|
||||
dn: uid=phi,ou=Robots,dc=unit,dc=macaomilano,dc=org
|
||||
objectClass: account
|
||||
objectClass: simpleSecurityObject
|
||||
objectClass: top
|
||||
uid: phi
|
||||
userPassword: {SHA}REu9CtcqSaA1c5J+sEYlTgg0H+M=
|
||||
|
||||
dn: uid=irc,ou=Services,dc=unit,dc=macaomilano,dc=org
|
||||
dn: uid=irc,ou=Robots,dc=unit,dc=macaomilano,dc=org
|
||||
objectClass: account
|
||||
objectClass: simpleSecurityObject
|
||||
objectClass: top
|
||||
uid: irc
|
||||
userPassword: {SHA}0WvxFW9MSsesf55SOh4vnuwdkgY=
|
||||
|
||||
dn: uid=git,ou=Services,dc=unit,dc=macaomilano,dc=org
|
||||
dn: uid=git,ou=Robots,dc=unit,dc=macaomilano,dc=org
|
||||
objectClass: account
|
||||
objectClass: simpleSecurityObject
|
||||
objectClass: top
|
||||
|
@ -47,9 +47,9 @@ objectClass: inetOrgPerson
|
|||
objectClass: organizationalPerson
|
||||
objectClass: person
|
||||
objectClass: top
|
||||
memberOf: cn=Admins,ou=Groups,dc=unit,dc=macaomilano,dc=org
|
||||
memberOf: cn=GitUsers,ou=Groups,dc=unit,dc=macaomilano,dc=org
|
||||
memberOf: cn=IRCUsers,ou=Groups,dc=unit,dc=macaomilano,dc=org
|
||||
memberOf: cn=Admins,ou=Congregations,dc=unit,dc=macaomilano,dc=org
|
||||
memberOf: cn=GitUsers,ou=Congregations,dc=unit,dc=macaomilano,dc=org
|
||||
memberOf: cn=IRCUsers,ou=Congregations,dc=unit,dc=macaomilano,dc=org
|
||||
cn: Raffaello
|
||||
sn: Mascetti
|
||||
mail: rmascetti@autistici.org
|
||||
|
@ -61,8 +61,8 @@ objectClass: inetOrgPerson
|
|||
objectClass: organizationalPerson
|
||||
objectClass: person
|
||||
objectClass: top
|
||||
memberOf: cn=GitUsers,ou=Groups,dc=unit,dc=macaomilano,dc=org
|
||||
memberOf: cn=IRCUsers,ou=Groups,dc=unit,dc=macaomilano,dc=org
|
||||
memberOf: cn=GitUsers,ou=Congregations,dc=unit,dc=macaomilano,dc=org
|
||||
memberOf: cn=IRCUsers,ou=Congregations,dc=unit,dc=macaomilano,dc=org
|
||||
cn: Guido
|
||||
sn: Necchi
|
||||
mail: gnecchi@autistici.org
|
||||
|
@ -74,20 +74,20 @@ objectClass: inetOrgPerson
|
|||
objectClass: organizationalPerson
|
||||
objectClass: person
|
||||
objectClass: top
|
||||
memberOf: cn=GitUsers,ou=Groups,dc=unit,dc=macaomilano,dc=org
|
||||
memberOf: cn=GitUsers,ou=Congregations,dc=unit,dc=macaomilano,dc=org
|
||||
cn: Giorgio
|
||||
sn: Perozzi
|
||||
mail: gperozzi@autistici.org
|
||||
uid: perozzi
|
||||
userPassword: {SHA}0+CRQKqsTj1I82PHxvZ4ebbddXQ=
|
||||
|
||||
dn: cn=Admins,ou=Groups,dc=unit,dc=macaomilano,dc=org
|
||||
dn: cn=Admins,ou=Congregations,dc=unit,dc=macaomilano,dc=org
|
||||
member: uid=conte_mascetti,ou=Hackers,dc=unit,dc=macaomilano,dc=org
|
||||
cn: Admins
|
||||
objectClass: groupOfNames
|
||||
objectClass: top
|
||||
|
||||
dn: cn=GitUsers,ou=Groups,dc=unit,dc=macaomilano,dc=org
|
||||
dn: cn=GitUsers,ou=Congregations,dc=unit,dc=macaomilano,dc=org
|
||||
member: uid=conte_mascetti,ou=Hackers,dc=unit,dc=macaomilano,dc=org
|
||||
member: uid=necchi,ou=Hackers,dc=unit,dc=macaomilano,dc=org
|
||||
member: uid=perozzi,ou=Hackers,dc=unit,dc=macaomilano,dc=org
|
||||
|
@ -95,7 +95,7 @@ cn: GitUsers
|
|||
objectClass: groupOfNames
|
||||
objectClass: top
|
||||
|
||||
dn: cn=IRCUsers,ou=Groups,dc=unit,dc=macaomilano,dc=org
|
||||
dn: cn=IRCUsers,ou=Congregations,dc=unit,dc=macaomilano,dc=org
|
||||
member: uid=conte_mascetti,ou=Hackers,dc=unit,dc=macaomilano,dc=org
|
||||
member: uid=necchi,ou=Hackers,dc=unit,dc=macaomilano,dc=org
|
||||
cn: IRCUsers
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
from bonsai import LDAPEntry, LDAPModOp, NoSuchObjectError # type: ignore
|
||||
from bonsai.ldapvaluelist import LDAPValueList
|
||||
import bonsai.errors
|
||||
|
||||
from phi.exceptions import (
|
||||
PhiAttributeMissing,
|
||||
|
@ -7,10 +9,10 @@ from phi.exceptions import (
|
|||
PhiEntryExists,
|
||||
PhiUnauthorized,
|
||||
PhiUnexpectedRuntimeValue,
|
||||
PhiCannotExecute,
|
||||
)
|
||||
|
||||
ATTR_TAG = "_ldap_"
|
||||
ATTR_TAG_LEN = len(ATTR_TAG)
|
||||
from phi.security import hash_pass, handle_password
|
||||
|
||||
|
||||
async def build_heritage(obj, child_class, attribute_id="uid"):
|
||||
|
@ -21,18 +23,20 @@ async def build_heritage(obj, child_class, attribute_id="uid"):
|
|||
async for child in obj.get_children():
|
||||
if attribute_id in child:
|
||||
_name = child[attribute_id][0]
|
||||
yield child_class(_name, obj.client)
|
||||
yield child_class(obj.client, _name)
|
||||
|
||||
|
||||
class Singleton(object):
|
||||
"""
|
||||
to singletonize a class. The class is crafted to be used with the mixins
|
||||
Mixin 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:
|
||||
def __new__(cls, client, *args, **kwargs):
|
||||
if "name" in kwargs:
|
||||
name = f"{cls.__name__}-{args['name']}-{id(client)}"
|
||||
elif args:
|
||||
name = f"{cls.__name__}-{args[0]}-{id(client)}"
|
||||
else:
|
||||
name = f"{cls.__name__}-{id(client)}"
|
||||
if name not in cls._instances:
|
||||
|
@ -40,13 +44,26 @@ class Singleton(object):
|
|||
return cls._instances[name]
|
||||
|
||||
|
||||
def get_value(obj, attr):
|
||||
"""
|
||||
Return the tuple (attribute_name, attribute_value) from obj. Extract the value,
|
||||
either it being a constant or the result of a function call.
|
||||
"""
|
||||
if not isinstance(getattr(type(obj), attr), property) and callable(
|
||||
getattr(type(obj), attr)
|
||||
):
|
||||
return attr, getattr(obj, attr)()
|
||||
else:
|
||||
return attr, getattr(obj, attr)
|
||||
|
||||
|
||||
class Entry(object):
|
||||
"""
|
||||
to interact with LDAP.
|
||||
Mixin to interact with LDAP.
|
||||
"""
|
||||
|
||||
def get_all_ldap_attributes(self):
|
||||
return [attr for attr in dir(self) if attr.startswith(ATTR_TAG)]
|
||||
return [get_value(self, attr) for attr in self.ldap_attributes]
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.__class__.__name__} {self.dn}>"
|
||||
|
@ -55,24 +72,17 @@ class Entry(object):
|
|||
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)
|
||||
for k, v in self.get_all_ldap_attributes():
|
||||
yield k, v
|
||||
|
||||
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())
|
||||
self._entry["objectClass"] = self.object_class
|
||||
async with self.client.connect(is_async=True) as conn:
|
||||
await conn.add(entry)
|
||||
return entry
|
||||
await conn.add(self._entry)
|
||||
return self._entry
|
||||
|
||||
async def _get(self):
|
||||
async with self.client.connect(is_async=True) as conn:
|
||||
|
@ -80,27 +90,50 @@ class Entry(object):
|
|||
# the one we are interested.
|
||||
_res = await conn.search(self.dn, 0)
|
||||
if len(_res) == 0:
|
||||
return
|
||||
raise PhiEntryDoesNotExist()
|
||||
elif len(_res) > 1:
|
||||
raise PhiUnexpectedRuntimeValue(
|
||||
"return value should be no more than one", res
|
||||
)
|
||||
return _res[0]
|
||||
|
||||
async def _modify(self):
|
||||
_ = await self._get()
|
||||
async with self.client.connect(is_async=True) as conn:
|
||||
self._entry.connection = conn
|
||||
await self._entry.modify()
|
||||
|
||||
async def _delete(self):
|
||||
async with self.client.connect(is_async=True) as conn:
|
||||
await conn.delete(self.dn)
|
||||
|
||||
async def describe(self):
|
||||
res = dict(await self._get())
|
||||
res["dn"] = self.dn
|
||||
res["name"] = self.name
|
||||
return res
|
||||
return dict(await self._get())
|
||||
|
||||
@property
|
||||
def delete_cascade(self):
|
||||
if hasattr(self, "_delete_cascade"):
|
||||
return self._delete_cascade
|
||||
return False
|
||||
|
||||
@delete_cascade.setter
|
||||
def delete_cascade(self, value):
|
||||
if not isinstance(value, bool):
|
||||
raise ValueError("delete_cascade must be a boolean")
|
||||
self._delete_cascade = value
|
||||
|
||||
|
||||
class OrganizationalUnit(object):
|
||||
def __init__(self, client):
|
||||
object_class = ["organizationalUnit", "top"]
|
||||
|
||||
def __init__(self, client, **kwargs):
|
||||
self.client = client
|
||||
self.base_dn = client.base_dn
|
||||
self.name = self.__class__.__name__
|
||||
self.children = build_heritage(self, getattr(self, "child_class"))
|
||||
self.children = build_heritage(self, self.child_class, self.child_class.id_tag)
|
||||
self._entry = LDAPEntry(self.dn)
|
||||
for k, v in kwargs.items():
|
||||
self._entry[k] = v
|
||||
|
||||
def __aiter__(self):
|
||||
return self
|
||||
|
@ -109,28 +142,117 @@ class OrganizationalUnit(object):
|
|||
try:
|
||||
return await self.children.__anext__()
|
||||
except StopAsyncIteration:
|
||||
self.children = build_heritage(self, getattr(self, "child_class"))
|
||||
self.children = build_heritage(
|
||||
self, self.child_class, self.child_class.id_tag
|
||||
)
|
||||
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):
|
||||
for el in await conn.search(self.dn, 1):
|
||||
yield el
|
||||
|
||||
@property
|
||||
def dn(self):
|
||||
return f"ou={self.ou_name},{self.base_dn}"
|
||||
return f"ou={self.ou},{self.base_dn}"
|
||||
|
||||
async def save(self):
|
||||
try:
|
||||
await self._create_new()
|
||||
except bonsai.errors.AlreadyExists:
|
||||
raise PhiEntryExists(self.dn)
|
||||
|
||||
async def search(self, member_name):
|
||||
result = None
|
||||
async with self.client.connect(is_async=True) as conn:
|
||||
result = await conn.search(
|
||||
f"{self.child_class.id_tag}={member_name},{self.dn}", 0
|
||||
)
|
||||
if not result:
|
||||
raise PhiEntryDoesNotExist(
|
||||
f"{self.child_class.id_tag}={member_name},{self.dn}"
|
||||
)
|
||||
if isinstance(result, list) and len(result) > 1:
|
||||
raise PhiUnexpectedRuntimeValue(
|
||||
"return value should be no more than one", result
|
||||
)
|
||||
return self.child_class(self.client, member_name, **result[0])
|
||||
|
||||
async def delete(self):
|
||||
"""
|
||||
Delete all the members of this OU only if `delete_cascade` is set to `True`,
|
||||
raises otherwise.
|
||||
"""
|
||||
if self.delete_cascade:
|
||||
async for member in self:
|
||||
await member.delete()
|
||||
else:
|
||||
raise PhiCannotExecute("Cannot delete an OU and delete_cascade is not set")
|
||||
|
||||
|
||||
class Child(object):
|
||||
object_class = ["inetOrgPerson", "organizationalPerson", "person", "top"]
|
||||
def hydrate(obj, data):
|
||||
for k, v in data.items():
|
||||
if k in obj.ldap_attributes:
|
||||
if isinstance(v, list) and not isinstance(v, LDAPValueList):
|
||||
obj._entry[k] = []
|
||||
for _v in v:
|
||||
obj._entry[k].append(_v.dn)
|
||||
elif k == "userPassword":
|
||||
obj._entry[k] = handle_password(v)
|
||||
else:
|
||||
obj._entry[k] = v
|
||||
|
||||
def __init__(self, client, name):
|
||||
|
||||
class Member(object):
|
||||
def __init__(self, client, name, **kwargs):
|
||||
self.client = client
|
||||
self.base_dn = client.base_dn
|
||||
self.name = name
|
||||
self._entry = LDAPEntry(self.dn)
|
||||
if kwargs:
|
||||
hydrate(self, kwargs)
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, type(self)):
|
||||
return other.dn == self.dn
|
||||
elif isinstance(other, str):
|
||||
return other == self.dn
|
||||
elif isinstance(other, LDAPEntry):
|
||||
return str(other) == self.dn
|
||||
else:
|
||||
return False
|
||||
|
||||
@property
|
||||
def dn(self):
|
||||
return f"{self.id_tag}={self.name},ou={self.ou_name},{self.base_dn}"
|
||||
return f"{self.id_tag}={self.name},ou={self.ou},{self.base_dn}"
|
||||
|
||||
def __setitem__(self, attr, val):
|
||||
if attr not in self.ldap_attributes:
|
||||
raise PhiCannotExecute(
|
||||
f"{attr} is not an allowed ldap_attribute: {self.ldap_attributes}"
|
||||
)
|
||||
self._entry[attr] = val
|
||||
|
||||
def __getitem__(self, attr):
|
||||
if attr not in self.ldap_attributes:
|
||||
raise PhiCannotExecute(
|
||||
f"{attr} is not an allowed ldap_attribute: {self.ldap_attributes}"
|
||||
)
|
||||
return self._entry[attr][0]
|
||||
|
||||
async def save(self):
|
||||
try:
|
||||
await self._create_new()
|
||||
except bonsai.errors.AlreadyExists:
|
||||
raise PhiEntryExists(self.dn)
|
||||
|
||||
async def modify(self):
|
||||
await self._modify()
|
||||
|
||||
async def delete(self):
|
||||
await self._delete()
|
||||
|
||||
async def sync(self):
|
||||
res = await self._get()
|
||||
hydrate(self, res)
|
||||
return self
|
||||
|
|
|
@ -1,15 +1,79 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from bonsai import LDAPEntry
|
||||
from multidict import MultiDict
|
||||
|
||||
from phi.async_ldap import mixins
|
||||
|
||||
|
||||
class User(mixins.Singleton, mixins.Entry, mixins.Child):
|
||||
_instances = dict()
|
||||
class User(mixins.Singleton, mixins.Entry, mixins.Member):
|
||||
object_class = [
|
||||
"inetOrgPerson",
|
||||
"simpleSecurityObject",
|
||||
"organizationalPerson",
|
||||
"person",
|
||||
"top",
|
||||
]
|
||||
_instances = dict() # type: ignore
|
||||
id_tag = "uid"
|
||||
ou_name = "Hackers"
|
||||
ou = "Hackers"
|
||||
ldap_attributes = ["uid", "ou", "cn", "sn", "mail", "userPassword"]
|
||||
|
||||
|
||||
class Hackers(mixins.Singleton, mixins.Entry, mixins.OrganizationalUnit):
|
||||
_instances = dict()
|
||||
ou_name = "Hackers"
|
||||
_instances = dict() # type: ignore
|
||||
ou = "Hackers"
|
||||
child_class = User
|
||||
|
||||
|
||||
class Service(mixins.Singleton, mixins.Entry, mixins.Member):
|
||||
object_class = ["simpleSecurityObject", "account", "top"]
|
||||
_instances = dict() # type: ignore
|
||||
id_tag = "uid"
|
||||
ou = "Robots"
|
||||
ldap_attributes = ["uid", "ou", "userPassword"]
|
||||
|
||||
|
||||
class Robots(mixins.Singleton, mixins.Entry, mixins.OrganizationalUnit):
|
||||
_instances = dict() # type: ignore
|
||||
ou = "Robots"
|
||||
child_class = Service
|
||||
|
||||
|
||||
class Group(mixins.Singleton, mixins.Entry, mixins.Member):
|
||||
object_class = ["groupOfNames", "top"]
|
||||
_instances = dict() # type: ignore
|
||||
id_tag = "cn"
|
||||
ou = "Congregations"
|
||||
ldap_attributes = ["uid", "ou", "member"]
|
||||
memeber_classes = {"Hackers": User, "Robots": Service}
|
||||
|
||||
async def add_member(self, member):
|
||||
self._entry["member"].append(get_dn(member))
|
||||
await self.modify()
|
||||
|
||||
async def remove_member(self, member):
|
||||
self._entry["member"] = [get_dn(m) for m in self.get_members() if member != m]
|
||||
await self.modify()
|
||||
|
||||
def get_members(self):
|
||||
for member in self._entry.get("member", []):
|
||||
dn = MultiDict(e.split("=") for e in member.split(","))
|
||||
yield self.memeber_classes.get(dn["ou"])(self.client, dn["uid"])
|
||||
|
||||
|
||||
class Congregations(mixins.Singleton, mixins.Entry, mixins.OrganizationalUnit):
|
||||
_instances = dict() # type: ignore
|
||||
ou = "Congregations"
|
||||
child_class = Group
|
||||
|
||||
|
||||
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}")
|
||||
|
|
|
@ -5,11 +5,23 @@ class PhiEntryExists(Exception):
|
|||
def __init__(self, dn):
|
||||
self.dn = dn
|
||||
|
||||
def __str__(self):
|
||||
return f"Entry exists yet ({self.dn})"
|
||||
|
||||
def __repr__(self):
|
||||
return f"PhiEntryExists({self.dn})"
|
||||
|
||||
|
||||
class PhiEntryDoesNotExist(Exception):
|
||||
def __init__(self, dn):
|
||||
self.dn = dn
|
||||
|
||||
def __str__(self):
|
||||
return f"Entry does not exist ({self.dn})"
|
||||
|
||||
def __repr__(self):
|
||||
return f"PhiEntryDoesNotExist({self.dn})"
|
||||
|
||||
|
||||
class PhiAttributeMissing(Exception):
|
||||
def __init__(self, dn, attr):
|
||||
|
@ -28,3 +40,15 @@ class PhiUnexpectedRuntimeValue(RuntimeWarning):
|
|||
super().__init__(self)
|
||||
self.msg = msg
|
||||
self.result = result
|
||||
|
||||
|
||||
class PhiCannotExecute(RuntimeWarning):
|
||||
def __init__(self, msg):
|
||||
super().__init__(self)
|
||||
self.msg = msg
|
||||
|
||||
def __str__(self):
|
||||
return f"Cannot execute: {self.msg}"
|
||||
|
||||
def __repr__(self):
|
||||
return f"PhiCannotExecute({self.msg})"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
from bonsai.ldapvaluelist import LDAPValueList
|
||||
from passlib.hash import (
|
||||
ldap_sha1,
|
||||
ldap_bcrypt,
|
||||
|
@ -8,7 +9,6 @@ from passlib.hash import (
|
|||
ldap_pbkdf2_sha512,
|
||||
)
|
||||
|
||||
|
||||
HASH_CALLABLE = {
|
||||
"sha1": ldap_sha1.hash,
|
||||
"bcrypt": ldap_bcrypt.hash,
|
||||
|
@ -21,3 +21,9 @@ HASH_CALLABLE = {
|
|||
|
||||
def hash_pass(password, method="sha1"):
|
||||
return HASH_CALLABLE[method](password)
|
||||
|
||||
|
||||
def handle_password(password, method="sha1"):
|
||||
if isinstance(password, LDAPValueList):
|
||||
return password
|
||||
return hash_pass(password, method)
|
||||
|
|
|
@ -3,19 +3,17 @@
|
|||
import asyncio
|
||||
from pprint import pprint as pp
|
||||
|
||||
from phi.async_ldap.model import (
|
||||
# Hackers,
|
||||
# User,
|
||||
from phi.async_ldap.new_model import (
|
||||
Hackers,
|
||||
User,
|
||||
Robots,
|
||||
Service,
|
||||
Congregations,
|
||||
Group,
|
||||
build_heritage,
|
||||
iter_children,
|
||||
Congregations,
|
||||
)
|
||||
|
||||
from phi.async_ldap.new_model import Hackers, User
|
||||
from phi.async_ldap.mixins import build_heritage
|
||||
from phi.async_ldap.client import AsyncClient
|
||||
import phi.exceptions as e
|
||||
|
||||
|
||||
async def dlv(h, cls):
|
||||
|
@ -47,15 +45,115 @@ async def get_all_children():
|
|||
return (hackers, robots, groups)
|
||||
|
||||
|
||||
async def print_async(awaitable):
|
||||
async def get_members(group):
|
||||
return [el async for el in group]
|
||||
|
||||
|
||||
async def print_async(label, awaitable):
|
||||
print(label)
|
||||
result = await awaitable
|
||||
pp(result)
|
||||
|
||||
|
||||
async def describe(obj):
|
||||
results = await obj.describe()
|
||||
return results
|
||||
return await obj.describe()
|
||||
|
||||
|
||||
asyncio.run(print_async(describe(Hackers(cl))))
|
||||
asyncio.run(print_async(describe(User(cl, "conte_mascetti"))))
|
||||
async def _await(awaitable):
|
||||
return await awaitable
|
||||
|
||||
|
||||
def sync_await(awaitable):
|
||||
return asyncio.run(_await(awaitable))
|
||||
|
||||
|
||||
h = Hackers(cl)
|
||||
r = Robots(cl)
|
||||
c = Congregations(cl)
|
||||
|
||||
|
||||
# asyncio.run(print_async("hackers:", describe(h)))
|
||||
# asyncio.run(print_async("conte_mascetti:", describe(User(cl, "conte_mascetti"))))
|
||||
# asyncio.run(print_async("robots:", describe(r)))
|
||||
# asyncio.run(print_async("phi:", describe(Service(cl, "phi"))))
|
||||
# asyncio.run(print_async("congregations:", describe(c)))
|
||||
# asyncio.run(print_async("GitUsers:", describe(Group(cl, "GitUsers"))))
|
||||
|
||||
# asyncio.run(print_async("Hackers members:", get_members(h)))
|
||||
# asyncio.run(print_async("Robots members:", get_members(r)))
|
||||
# asyncio.run(print_async("Congregations members:", get_members(c)))
|
||||
|
||||
#
|
||||
async def add_new(obj, name, **kw):
|
||||
try:
|
||||
_new = obj(cl, name, **kw)
|
||||
await _new.save()
|
||||
except e.PhiEntryExists as err:
|
||||
print(f"Failed add: {repr(err)}")
|
||||
|
||||
|
||||
async def safe_search(group, name):
|
||||
try:
|
||||
res = await group.search(name)
|
||||
print("Search result:", res)
|
||||
return res
|
||||
except e.PhiEntryDoesNotExist as err:
|
||||
print(f"Failed search: {repr(err)}")
|
||||
|
||||
|
||||
async def safe_delete(obj, cascade=None):
|
||||
try:
|
||||
if cascade:
|
||||
obj.delete_cascade = cascade
|
||||
await obj.delete()
|
||||
except Exception as err:
|
||||
print(f"Failed delete: {repr(err)}")
|
||||
|
||||
|
||||
async def add_member(group, member):
|
||||
await group.add_member(member)
|
||||
|
||||
|
||||
async def remove_member(group, member):
|
||||
await group.remove_member(member)
|
||||
|
||||
|
||||
# asyncio.run(safe_search(h, "pippo"))
|
||||
# asyncio.run(
|
||||
# add_new(User, "pippo", cn="Pippo (Goofy)", sn="Pippo", mail="pippo@unit.info")
|
||||
# )
|
||||
# asyncio.run(safe_search(h, "pippo"))
|
||||
# asyncio.run(
|
||||
# add_new(User, "pippo", cn="Pippo (Goofy)", sn="Pippo", mail="pippo@unit.net")
|
||||
# )
|
||||
# asyncio.run(safe_delete(asyncio.run(safe_search(h, "pippo"))))
|
||||
# asyncio.run(print_async("Hackers members:", get_members(h)))
|
||||
# asyncio.run(safe_delete(h))
|
||||
# asyncio.run(print_async("Hackers members:", get_members(h)))
|
||||
# asyncio.run(safe_delete(h, True))
|
||||
# asyncio.run(print_async("Hackers members:", get_members(h)))
|
||||
|
||||
# asyncio.run(safe_search(r, "phi"))
|
||||
# asyncio.run(print_async("Robots members:", get_members(r)))
|
||||
# asyncio.run(add_new(Service, "db", userPassword="lolpassword"))
|
||||
# asyncio.run(print_async("Robots members:", get_members(r)))
|
||||
|
||||
asyncio.run(safe_search(c, "GitUsers"))
|
||||
asyncio.run(print_async("Congregations members:", get_members(c)))
|
||||
asyncio.run(
|
||||
add_new(Group, "naughty", member=[User(cl, "conte_mascetti"), User(cl, "necchi")])
|
||||
)
|
||||
asyncio.run(print_async("Congregations members:", get_members(c)))
|
||||
asyncio.run(safe_delete(Group(cl, "naughty")))
|
||||
asyncio.run(print_async("Congregations members:", get_members(c)))
|
||||
asyncio.run(
|
||||
add_new(Group, "naughty", member=[User(cl, "conte_mascetti"), User(cl, "necchi")])
|
||||
)
|
||||
asyncio.run(print_async("Congregations members:", get_members(c)))
|
||||
print("==> HERE <==")
|
||||
naughty = sync_await(Group(cl, "naughty").sync())
|
||||
print("NAUGHTY =>>", [m for m in naughty.get_members()])
|
||||
asyncio.run(add_member(naughty, User(cl, "perozzi")))
|
||||
print("NAUGHTY =>>", [m for m in naughty.get_members()])
|
||||
asyncio.run(remove_member(naughty, User(cl, "conte_mascetti")))
|
||||
print("NAUGHTY =>>", [m for m in naughty.get_members()])
|
||||
|
|
Loading…
Reference in New Issue
Block a user