from ldap3 import Server, ALL, Connection, MODIFY_REPLACE, SIMPLE, AUTO_BIND_NO_TLS, SASL

from ..Playbooks.Playbook import PlaybookCredentialCollection, FileCleanupTask
from ...Logging.Logger import log, Severity
from ...Machine.PaedMLMachineCollection import MACHINE_COLLECTION, LMZ_COMMON_CREDENTIALS
from ..SetupStep import SetupStep

class SetRawLdapPassword(SetupStep):
    def __init__(self, ldap_path: str, password:str):
        super().__init__()
        self.ldap_path = ldap_path
        self.password = password

    def run(self, file_cleanup_stack : list[FileCleanupTask], credentials: PlaybookCredentialCollection) -> bool:
        log(f'Changing password for {self.ldap_path}...', Severity.INFO)
        stdout, stderr, retval = MACHINE_COLLECTION.server.execute_shell_command('mkpasswd', '--hash=md5', self.password)
        if retval != 0:
            log(f'Failed to generate hash: {stderr}', Severity.ERROR)
            return False

        hash = stdout.strip()
        admin_user = f'cn=admin,{LMZ_COMMON_CREDENTIALS.get_ldap_base()}'
        log(f'Hash: {hash}', Severity.TRACE)

        try:
            ldap_server = Server(MACHINE_COLLECTION.server.get_hostname(), get_info=ALL, use_ssl=True, port=7636)
            ldap_connection = Connection(ldap_server, user=admin_user, password=credentials.ldap_admin,
                                         authentication=SIMPLE, raise_exceptions=True)
            ldap_connection.open()
            ldap_connection.start_tls()
            ldap_connection.bind()
            ldap_connection.modify(dn=self.ldap_path, changes={
                'userPassword': (MODIFY_REPLACE, '{crypt}' + hash)
            })
        except Exception as e:
            log(f'Failed to modify LDAP user: {e}', Severity.ERROR)
            return False

        log('User modified.', Severity.DEBUG)
        ldap_connection.unbind()
        return True