From 55db396a58ab145e8b92dbb191563a35c2e719e2 Mon Sep 17 00:00:00 2001 From: Frank Brehm Date: Thu, 22 Sep 2022 14:15:04 +0200 Subject: [PATCH] Asking first questions in bin/set-ldap-password --- lib/pp_admintools/app/set_ldap_password.py | 134 ++++++++++++++++++--- 1 file changed, 116 insertions(+), 18 deletions(-) diff --git a/lib/pp_admintools/app/set_ldap_password.py b/lib/pp_admintools/app/set_ldap_password.py index 22382e7..7231a60 100644 --- a/lib/pp_admintools/app/set_ldap_password.py +++ b/lib/pp_admintools/app/set_ldap_password.py @@ -10,26 +10,25 @@ from __future__ import absolute_import # Standard modules import logging import os -import pwd import getpass # Third party modules -from ldap3 import MODIFY_REPLACE, MODIFY_ADD, MODIFY_DELETE +# from ldap3 import MODIFY_REPLACE, MODIFY_ADD, MODIFY_DELETE +from ldap3.core.exceptions import LDAPBindError import passlib.apps # Own modules -from fb_tools.common import to_bool, is_sequence, pp +# from fb_tools.common import to_bool, is_sequence, pp +from fb_tools.common import is_sequence from ..xlate import XLATOR -from . import AbortAppError, TimeoutOnPromptError - -from .ldap import LdapAppError, FatalLDAPError +from .ldap import LdapAppError from .ldap import BaseLdapApplication from .ldap import PasswordFileOptionAction -__version__ = '0.2.0' +__version__ = '0.3.1' LOG = logging.getLogger(__name__) _ = XLATOR.gettext @@ -116,8 +115,6 @@ class SetLdapPasswordApplication(BaseLdapApplication): # ------------------------------------------------------------------------- def init_arg_parser(self): - super(SetLdapPasswordApplication, self).init_arg_parser() - app_group = self.arg_parser.add_argument_group(_("Options for {}").format( self.appname)) @@ -125,7 +122,7 @@ class SetLdapPasswordApplication(BaseLdapApplication): pw_group.add_argument( '-w', '--password', metavar=_("PASSWORD"), dest="current_pw", - help=_("Use PASSWORD as the current user password."), + help=_("Use {} as the current user password.").format(_("PASSWORD")), ) pw_group.add_argument( @@ -138,13 +135,20 @@ class SetLdapPasswordApplication(BaseLdapApplication): pw_group.add_argument( '-y', '--password-file', metavar=_('PASSWORD_FILE'), dest="current_pw_file", must_absolute=False, action=PasswordFileOptionAction, - help=_("Use contents of PASSWORD_FILE as the current user password."), + help=_("Use contents of {} as the current user password.").format(_('PASSWORD_FILE')), + ) + + app_group.add_argument( + '-n', '--new-password', metavar=_("PASSWORD"), dest="new_pw", + help=_( + "Use {} as the new user password. If not given, it will be " + "asked for it.").format(_("PASSWORD")), ) user_help = _( - "The user, which password in the given LDAP instance should be changed. " - "It may be given by its Uid (the alphanumeric POSIX name), its mail address " - "or its LDAP DN.") + "The user, which password in the given LDAP instance should be changed. " + "It may be given by its Uid (the alphanumeric POSIX name), its mail address " + "or its LDAP DN.") if self.current_user: user_help += ' ' + _( "If not given, then your current user name {!r} will be used.").format( @@ -160,6 +164,8 @@ class SetLdapPasswordApplication(BaseLdapApplication): app_group.add_argument( 'user', metavar=_('USER'), help=user_help) + super(SetLdapPasswordApplication, self).init_arg_parser() + # ------------------------------------------------------------------------- def post_init(self): """ @@ -195,6 +201,9 @@ class SetLdapPasswordApplication(BaseLdapApplication): self.current_password = self.read_password_file(self.args.current_pw_file) self.do_user_bind = True + if self.args.new_pw: + self.new_password = self.args.new_pw + inst = self.ldap_instances[0] ldap = self.cfg.ldap_connection[inst] if not ldap.is_admin or ldap.readonly: @@ -206,19 +215,108 @@ class SetLdapPasswordApplication(BaseLdapApplication): LOG.debug("Pre running tasks ...") super(SetLdapPasswordApplication, self).pre_run() + # ------------------------------------------------------------------------- + def _run(self): + + inst = self.ldap_instances[0] + connect_info = self.cfg.ldap_connection[inst] + msg = _("Using LDAP instance {inst!r} - {url}.").format(inst=inst, url=connect_info.url) + LOG.info(msg) + + self.user_dn = self.search_user_dn() + if self.do_user_bind and not self.current_password: first_prompt = _("Current password of user {!r}:").format(self.user_uid) + ' ' second_prompt = _('Repeat password:') + ' ' self.current_password = self.get_password( first_prompt, second_prompt, may_empty=False, repeat=False) + if self.do_user_bind: + self.test_user_bind() + + if not self.new_password: + first_prompt = _("New password of user {!r}:").format(self.user_uid) + ' ' + second_prompt = _('Repeat password:') + ' ' + self.new_password = self.get_password( + first_prompt, second_prompt, may_empty=False, repeat=True) + # ------------------------------------------------------------------------- - def _run(self): + def test_user_bind(self): inst = self.ldap_instances[0] - ldap = self.cfg.ldap_connection[inst] - msg = _("Using LDAP instance {inst!r} - {url}.").format(inst=inst, url=ldap.url) - LOG.info(msg) + connect_info = self.cfg.ldap_connection[inst] + + msg = _( + "Testing connect to LDAP-Server {url} with current user {dn!r} and " + "password ...").format(url=connect_info.url, dn=self.user_dn) + LOG.debug(msg) + + ldap_server = None + ldap_connection = None + + try: + ldap_server = self.get_ldap_server_obj(inst) + ldap_connection = self.connect_to_ldap_server( + ldap_server, inst, bind_dn=self.user_dn, bind_pw=self.current_password) + msg = _("Successful connected as {dn!r} to {url}.").format( + url=connect_info.url, dn=self.user_dn) + LOG.debug(msg) + + except LDAPBindError as e: + msg = _("Could not connect to {url} as {dn!r}: {e}").format( + url=connect_info.url, dn=self.user_dn, e=e) + self.exit(6, msg) + + finally: + if ldap_connection: + if self.verbose > 1: + LOG.debug(_("Unbinding from LDAP server {!r} ...").format(connect_info.url)) + ldap_connection.unbind() + ldap_connection = None + del ldap_connection + + if ldap_server: + if self.verbose > 1: + LOG.debug(_("Disconnecting from LDAP server {!r} ...").format( + connect_info.url)) + del ldap_server + + # ------------------------------------------------------------------------- + def search_user_dn(self): + """Searching the LDAP DN of the user, whos password should be changed.""" + + inst = self.ldap_instances[0] + connect_info = self.cfg.ldap_connection[inst] + + dns = self.get_user_dn(self.user_uid, inst) + if dns: + if self.verbose > 2: + msg = _("Got DN {dn!r} for user {user!r} in LDAP instance {inst}.").format( + dn=dns, user=self.user_uid, inst=connect_info.url) + LOG.debug(msg) + if is_sequence(dns): + if len(dns) > 1: + msg = _("Found {nr} entries for user {u!r} in LDAP instance {i}.").format( + nr=len(dns), u=self.user_uid, i=connect_info.url) + msg += ' ' + _( + "Please use another username, or use the correct DN from the following " + "list as a parameter for this script instead of the username:") + for dn in dns: + msg += '\n * ' + dn + LOG.warn(msg) + self.exit(7) + return + self.user_dn = dns[0] + else: + self.user_dn = dns + else: + msg = _("Did not found user {user!r} in LDAP instance {inst}.").format( + user=self.user_uid, inst=connect_info.url) + LOG.error(msg) + self.exit(7) + + LOG.info(_("Changing the password of user {dn!r} in LDAP instance {inst}.").format( + dn=self.user_dn, inst=connect_info.url)) # ============================================================================= -- 2.39.5