138 lines
5.3 KiB
Python
138 lines
5.3 KiB
Python
|
import ldap3
|
||
|
|
||
|
MAXENTRIES = 1000000
|
||
|
OBJECTCLASSES = ['top', 'person', 'organizationalPerson', 'inetOrgPerson', 'posixAccount', 'shadowAccount']
|
||
|
USERATTRIBUTES = ['cn' , 'sn', 'givenName', 'uid', 'uidNumber' , 'gidNumber', 'homeDirectory', 'loginShell', 'gecos' , 'shadowLastChange', 'shadowMax', 'userPassword', 'mail', 'description']
|
||
|
|
||
|
class ldapbind():
|
||
|
def __init__(self, ldap_host: str, admin_user: str, admin_pass: str, base: str):
|
||
|
self.ldap_host = ldap_host
|
||
|
self.admin_user = admin_user
|
||
|
self.admin_pass= admin_pass
|
||
|
self.base = base
|
||
|
|
||
|
ldapserver = ldap3.Server(self.ldap_host,use_ssl=True)
|
||
|
self.conn = ldap3.Connection(ldapserver, admin_user, admin_pass, auto_bind=True)
|
||
|
|
||
|
class logbranch():
|
||
|
def __init__(self, ldap_server: ldapbind, log_server: ldapbind):
|
||
|
|
||
|
self.ldap_server = ldap_server
|
||
|
self.log_server = log_server
|
||
|
|
||
|
self.base = self.ldap_server.base
|
||
|
self.logbase = self.log_server.base
|
||
|
self.logconnection = self.log_server.conn
|
||
|
|
||
|
self.ldapconnection = self.ldap_server.conn
|
||
|
self.logconnection = self.log_server.conn
|
||
|
|
||
|
# How many changes we applied to the LDAP server
|
||
|
self.loaded = self.getloaded()
|
||
|
|
||
|
# How many changes are recoreded in total
|
||
|
self.total = self.gettotal()
|
||
|
|
||
|
def gettotal(self)->int:
|
||
|
self.logconnection.search(search_base=f'uid=total,{self.logbase}',search_filter='(objectClass=person)', attributes=['uidNumber'])
|
||
|
response = self.logconnection.response
|
||
|
if response == []:
|
||
|
response = 0
|
||
|
return int(response['attributes']['uidNumber'])
|
||
|
|
||
|
def settotal(self, newvalue: int):
|
||
|
newvalue = int(newvalue)
|
||
|
|
||
|
# Check if total record already present
|
||
|
self.logconnection.search(search_base=f'uid=total,{self.logbase}',search_filter='(objectClass=person)', attributes=['uidNumber'])
|
||
|
response = self.logconnection.response
|
||
|
|
||
|
if response == []:
|
||
|
return self.logconnection.add(f'uid=total,{self.logbase}', OBJECTCLASSES, { 'uid' : 'total', 'uidNumber' : 0 })
|
||
|
|
||
|
return self.logconnection.modify(f'uid=total,{self.logbase}', {'uidNumber' : (ldap3.MODIFY_REPLACE, [newvalue])})
|
||
|
|
||
|
def getloaded(self):
|
||
|
self.connection.search(search_base=f'uid=loaded,{self.logbase}',search_filter='(objectClass=person)', attributes=['uidNumber'])
|
||
|
response = self.logconnection.response
|
||
|
if response == []:
|
||
|
response = 0
|
||
|
return int(self.connection.response['attributes']['uidNumber'])
|
||
|
|
||
|
def setloaded(self, newvalue: int):
|
||
|
newvalue = int(newvalue)
|
||
|
|
||
|
# Check if loaded record already present
|
||
|
self.logconnection.search(search_base=f'uid=loaded,{self.logbase}',search_filter='(objectClass=person)', attributes=['uidNumber'])
|
||
|
response = self.logconnection.response
|
||
|
|
||
|
if response == []:
|
||
|
return self.logconnection.add(f'uid=loaded,{self.logbase}', OBJECTCLASSES, { 'uid' : 'loaded', 'uidNumber' : 0 })
|
||
|
|
||
|
return self.logconnection.modify(f'uid=loaded,{self.logbase}', {'uidNumber' : (ldap3.MODIFY_REPLACE, [newvalue])})
|
||
|
|
||
|
def refreshtotal(self):
|
||
|
self.settotal(self.findtotal())
|
||
|
return self.gettotal()
|
||
|
|
||
|
def findtotal(self):
|
||
|
for entry in range(self.gettotal(), MAXENTRIES):
|
||
|
self.logconnection.search(search_base=f'uid={entry},{self.logbase}',search_filter='(objectClass=person)', attributes=['uidNumber'])
|
||
|
if self.logconnection.response == []:
|
||
|
return entry - 1
|
||
|
|
||
|
def applylogs(self):
|
||
|
self.refreshtotal()
|
||
|
|
||
|
loaded = int(self.getloaded()) + 1
|
||
|
total = int(self.gettotal()) + 1
|
||
|
|
||
|
for log_number in range(loaded, total):
|
||
|
self.setlog(log_number)
|
||
|
|
||
|
return total - loaded
|
||
|
|
||
|
def applylog(self, log_number: int):
|
||
|
log = self.getlog(log_number)
|
||
|
return self.setlog(self, log)
|
||
|
|
||
|
def getlog(self, log_number: int):
|
||
|
self.logconnection.search(search_base=f'uid={log_number},{self.logbase}',search_filter = '(objectClass=person)', attributes = USERATTRIBUTES)
|
||
|
return logconnection.response
|
||
|
|
||
|
def setlog(self, log_number: int):
|
||
|
log = self.getlog(log_number)
|
||
|
|
||
|
attributes = log['attributes']['description']
|
||
|
uid = attributes['uid']
|
||
|
action = attributes['description']
|
||
|
attributes['description'] = ''
|
||
|
|
||
|
if action == 'ADD':
|
||
|
self.ldapconnection.add(f'uid={uid},{self.base}', OBJECTCLASSES, attributes)
|
||
|
elif action == 'DELETE':
|
||
|
self.ldapconnection.delete(f'uid={uid},{self.base}')
|
||
|
elif action == 'CHANGEPASS':
|
||
|
self.ldapconnection.modify(f'uid={uid},{self.base}', {'userPassword': (MODIFY_REPLACE,attributes['userPassword'])})
|
||
|
else:
|
||
|
return f'Error: Unrecognized action in log entry: {action}'
|
||
|
|
||
|
self.setloaded( self.getloaded() + 1 )
|
||
|
return ldapconnection.response
|
||
|
|
||
|
class logtree():
|
||
|
def __init__(self, ldap_server: ldapbind):
|
||
|
self.ldap_server = ldap_server
|
||
|
self.branches = []
|
||
|
|
||
|
def add(self, log_server: logbranch):
|
||
|
self.branches.append(log_server)
|
||
|
|
||
|
def remove(self, log_server: logbranch):
|
||
|
self.branches.remove(log_server)
|
||
|
|
||
|
def sync(self):
|
||
|
for branch in self.branches:
|
||
|
branch.applylogs()
|
||
|
|