import json from typing import List, Union import ldap3 from zope import component from zope.interface import implementer from .utils import dn_to_uid from ceo_common.interfaces import ILDAPService, IGroup @implementer(IGroup) class Group: def __init__( self, cn: str, gid_number: int, members: Union[List[str], None] = None, ldap3_entry: Union[ldap3.Entry, None] = None, ): self.cn = cn self.gid_number = gid_number # this is a list of the usernames of the members in this group self.members = members or [] self.ldap3_entry = ldap3_entry self.ldap_srv = component.getUtility(ILDAPService) def __repr__(self) -> str: return json.dumps(self.to_dict(), indent=2) def to_dict(self): return { 'cn': self.cn, 'gid_number': self.gid_number, 'members': self.members, } def add_to_ldap(self): self.ldap_srv.add_group(self) def remove_from_ldap(self): self.ldap_srv.remove_group(self) @staticmethod def deserialize_from_ldap(entry: ldap3.Entry) -> IGroup: """Deserialize this group from an LDAP entry.""" attrs = entry.entry_attributes_as_dict return Group( cn=attrs['cn'][0], gid_number=attrs['gidNumber'][0], members=[ dn_to_uid(dn) for dn in attrs.get('uniqueMember', []) ], ldap3_entry=entry, ) def add_member(self, username: str): dn = self.ldap_srv.uid_to_dn(username) with self.ldap_srv.entry_ctx_for_group(self) as entry: entry.uniqueMember.add(dn) self.members.append(username) def remove_member(self, username: str): dn = self.ldap_srv.uid_to_dn(username) with self.ldap_srv.entry_ctx_for_group(self) as entry: entry.uniqueMember.delete(dn) self.members.remove(username)