]> Frank Brehm's Git Trees - pixelpark/pp-admin-tools.git/commitdiff
Using LdapPasswordHandler in bin/set-ldap-password
authorFrank Brehm <frank@brehm-online.com>
Thu, 10 Nov 2022 17:54:44 +0000 (18:54 +0100)
committerFrank Brehm <frank@brehm-online.com>
Thu, 10 Nov 2022 17:54:44 +0000 (18:54 +0100)
lib/pp_admintools/app/set_ldap_password.py
lib/pp_admintools/handler/ldap_password.py

index e40f06c6b8c8f6a55b0fc2b307f42c3db0f7019e..b2aa9e0853408b678e961ab8ae2c562acf0a1f59 100644 (file)
@@ -17,15 +17,6 @@ import getpass
 from ldap3 import MODIFY_REPLACE
 from ldap3.core.exceptions import LDAPBindError
 
-import passlib.apps
-
-HAS_CRACKLIB = False
-try:
-    import cracklib
-    HAS_CRACKLIB = True
-except ImportError:
-    pass
-
 # Own modules
 # from fb_tools.common import to_bool, is_sequence, pp
 from fb_tools.common import is_sequence
@@ -38,7 +29,11 @@ from .ldap import LdapAppError, FatalLDAPError
 from .ldap import BaseLdapApplication
 from .ldap import PasswordFileOptionAction
 
-__version__ = '0.6.6'
+from ..handler.ldap_password import WrongPwdSchemaError
+from ..handler.ldap_password import LdapPasswordHandler
+from ..handler.ldap_password import HAS_CRACKLIB
+
+__version__ = '0.7.1'
 LOG = logging.getLogger(__name__)
 
 _ = XLATOR.gettext
@@ -69,80 +64,10 @@ class SetLdapPasswordApplication(BaseLdapApplication):
     except KeyError:
         pass
 
-    possible_schemes = (
-        'ldap_des_crypt',
-        'ldap_md5',
-        'ldap_md5_crypt',
-        'ldap_salted_md5',
-        'ldap_sha1',
-        'ldap_sha1_crypt',
-        'ldap_salted_sha1',
-        'ldap_sha256_crypt',
-        'ldap_salted_sha256',
-        'ldap_sha512_crypt',
-        'ldap_salted_sha512',
-        'ldap_pbkdf2_sha512',
-    )
-
-    ldap_context = passlib.apps.ldap_context
-    available_schemes = []
-
-    schema_ids = {
-        'ldap_des_crypt': 'CRYPT',
-        'ldap_md5': 'MD5',
-        'ldap_md5_crypt': 'CRYPT-MD5',
-        'ldap_salted_md5': 'SMD5',
-        'ldap_sha1': 'SHA',
-        'ldap_sha1_crypt': 'SHA-CRYPT',
-        'ldap_salted_sha1': 'SSHA',
-        'ldap_sha256_crypt': 'CRYPT-SHA256',
-        'ldap_salted_sha256': 'SSHA256',
-        'ldap_sha512_crypt': 'CRYPT-SHA512',
-        'ldap_salted_sha512': 'SSHA512',
-        'ldap_pbkdf2_sha512': 'PBKDF2_SHA512',
-    }
-
-    schema_description = {
-        'ldap_des_crypt': _('The ancient and notorious 3 DES crypt method.'),
-        'ldap_md5': _('Pure {} hashing method.').format('MD5'),
-        'ldap_md5_crypt': _("A {} based hashing algorithm.").format('MD5'),
-        'ldap_salted_md5': _("Salted {} hashing method.").format('MD5'),
-        'ldap_sha1': _('Pure {} hashing method.').format('SHA-1'),
-        'ldap_sha1_crypt': _("A {} based hashing algorithm.").format('SHA-1'),
-        'ldap_salted_sha1': _("Salted {} hashing method.").format('SHA-1'),
-        'ldap_sha256_crypt': _("A {} based hashing algorithm.").format('SHA-256'),
-        'ldap_salted_sha256': _("Salted {} hashing method.").format('SHA-256'),
-        'ldap_sha512_crypt': _("A {} based hashing algorithm.").format('SHA-512'),
-        'ldap_salted_sha512': _("Salted {} hashing method.").format('SHA-512'),
-        'ldap_pbkdf2_sha512': _(
-            "A hashing method derived from {} with additional computing rounds.").format(
-            'SHA-512'),
-    }
-
-    passlib_context = None
-    default_schema = 'ldap_sha512_crypt'
-    default_schema_id = 'CRYPT-SHA512'
-    default_pbkdf2_rounds = 30000
-
-    # -------------------------------------------------------------------------
-    @classmethod
-    def init_pass_schemes(cls):
-
-        cls.available_schemes = []
-        all_handlers = passlib.registry.list_crypt_handlers()
-
-        for schema in cls.possible_schemes:
-            if schema in all_handlers:
-                cls.available_schemes.append(schema)
-
-        cls.passlib_context = passlib.context.CryptContext(
-            schemes=cls.available_schemes, ldap_pbkdf2_sha512__rounds=cls.default_pbkdf2_rounds)
-        cls.passlib_context.update(default=cls.default_schema)
-
     # -------------------------------------------------------------------------
     def __init__(self, appname=None, base_dir=None):
 
-        self.init_pass_schemes()
+        LdapPasswordHandler.init_pass_schemes()
 
         self.current_password = None
         self.need_current_password = False
@@ -152,12 +77,13 @@ class SetLdapPasswordApplication(BaseLdapApplication):
         self.new_password = None
         self.user_uid = None
         self.user_dn = None
-        self.schema = self.default_schema
-        self.schema_id = self.default_schema_id
         self.no_cracklib = False
 
         self.user_connection = None
 
+        self.pwd_handler = LdapPasswordHandler(
+            appname=appname, base_dir=base_dir, initialized=False)
+
         my_appname = self.get_generic_appname(appname)
 
         desc = _(
@@ -237,12 +163,13 @@ class SetLdapPasswordApplication(BaseLdapApplication):
 
         schema_list = []
         def_schema = ''
-        for method in self.available_schemes:
-            schema_id = self.schema_ids[method]
+        for method in LdapPasswordHandler.available_schemes:
+            schema_id = LdapPasswordHandler.schema_ids[method]
             schema_list.append(schema_id)
-            if method == self.default_schema:
+            if method == LdapPasswordHandler.default_schema:
                 def_schema = schema_id
         schema_list.append('list')
+        schema_list.append('help')
 
         help_txt1 = _(
             "The schema (hashing method) to use to hash the new password. It is possible to give "
@@ -291,22 +218,24 @@ class SetLdapPasswordApplication(BaseLdapApplication):
             msg = "Given args:\n" + pp(self.args.__dict__)
             LOG.debug(msg)
 
+        self.pwd_handler.verbose = self.verbose
+        self.pwd_handler.simulate = self.simulate
+        self.pwd_handler.force = self.force
+        self.pwd_handler.terminal_has_colors = self.terminal_has_colors
+        self.pwd_handler.initialized = True
+
         self.no_cracklib = getattr(self.args, 'no_cracklib', False)
 
         given_schema = getattr(self.args, 'schema', None)
         if given_schema:
-            if given_schema == 'list':
-                self._show_hashing_schemes()
+            if given_schema in ('list', 'help'):
+                self.pwd_handler.show_hashing_schemes()
                 self.exit(0)
                 return
-            for method in self.available_schemes:
-                schema_id = self.schema_ids[method]
-                LOG.debug("Testing for {m!r} ({s}) ...".format(m=method, s=schema_id))
-                if schema_id == given_schema:
-                    self.passlib_context.update(default=method)
-                    self.schema = method
-                    self.schema_id = schema_id
-                    break
+            try:
+                self.pwd_handler.set_schema_by_id(given_schema)
+            except WrongPwdSchemaError as e:
+                self.exit(5, str(e))
 
         given_user = getattr(self.args, 'user', None)
         if given_user:
@@ -351,36 +280,6 @@ class SetLdapPasswordApplication(BaseLdapApplication):
                 LOG.debug("User bind: LDAP instance is readonly or not as admin.")
             self.do_user_bind = True
 
-    # -------------------------------------------------------------------------
-    def _show_hashing_schemes(self):
-
-        max_len_schema = 1
-        for method in self.available_schemes:
-            schema_id = self.schema_ids[method]
-            if len(schema_id) > max_len_schema:
-                max_len_schema = len(schema_id)
-
-        title = _("Usable Hashing schemes:")
-        print(title)
-        print('-' * len(title))
-        print()
-
-        for method in self.available_schemes:
-            schema_id = self.schema_ids[method]
-            desc = self.schema_description[method]
-            if 'pbkdf2' in method:
-                desc += ' ' + _(
-                    "This schema cannot be used for authentication on a "
-                    "current freeradius server.")
-            if method == self.schema:
-                desc += ' ' + _("This is the default schema.")
-
-            line = ' * {id:<{max_len}} - '.format(id=schema_id, max_len=max_len_schema)
-            line += desc
-            print(line)
-
-        print()
-
     # -------------------------------------------------------------------------
     def pre_run(self):
 
@@ -417,24 +316,14 @@ class SetLdapPasswordApplication(BaseLdapApplication):
             self.new_password = self.get_password(
                 first_prompt, second_prompt, may_empty=False, repeat=True)
 
-        if HAS_CRACKLIB:
-            if self.no_cracklib:
-                msg = _("Checking the quality of the new password was disabled.")
-                LOG.warn(msg)
-            else:
-                LOG.info(_("Testing quality of new password ..."))
-                try:
-                    cracklib.VeryFascistCheck(self.new_password)
-                except ValueError as e:
-                    msg = _("Quality of the new password is not sufficient:") + ' ' + str(e)
-                    LOG.error(msg)
-                    self.exit(1)
-                LOG.debug("The quality of the new password seems to be sufficient.")
-        else:
-            msg = _(
-                "Cannot testing the quality of the new password, because the "
-                "Python module {!r} is not installed.").format('cracklib')
+        if self.no_cracklib:
+            msg = _("Checking the quality of the new password was disabled.")
             LOG.warn(msg)
+        else:
+            if self.pwd_handler.check_password_quality(self.new_password):
+                LOG.debug("The quality of the new password seems to be sufficient.")
+            else:
+                self.exit(1)
 
         super(SetLdapPasswordApplication, self).pre_run()
 
@@ -576,7 +465,7 @@ class SetLdapPasswordApplication(BaseLdapApplication):
         print(msg)
 
         LOG.debug(_("Used schema: {!r}.").format(self.schema))
-        hashed_passwd = self.passlib_context.hash(self.new_password, self.schema)
+        hashed_passwd = self.pwd_handler.get_hash(self.new_password, self.schema)
         msg = _("New password hash: '{}'.").format(self.colored(hashed_passwd, 'CYAN'))
         print(msg)
 
index dc3115d1343e85c0aab84c26fc3caa25397289e2..0c5dbfd0a37098fb2ead574fa4fdde765e919293 100644 (file)
@@ -143,15 +143,21 @@ class LdapPasswordHandler(HandlingObject):
     # -------------------------------------------------------------------------
     def __init__(
         self, appname=None, verbose=0, version=__version__, base_dir=None,
-            simulate=None, force=None, assumed_answer=None,
+            simulate=False, force=False, assumed_answer=None,
             terminal_has_colors=False, initialized=False):
 
+        self.schema = self.default_schema
+        self.schema_id = self.default_schema_id
+
         super(LdapPasswordHandler, self).__init__(
             appname=appname, verbose=verbose, version=version, base_dir=base_dir,
             simulate=simulate, force=force, assumed_answer=assumed_answer,
             terminal_has_colors=terminal_has_colors, initialized=False,
         )
 
+        if initialized:
+            self.initialized = True
+
     # -------------------------------------------------------------------------
     def as_dict(self, short=True):
         """
@@ -268,7 +274,10 @@ class LdapPasswordHandler(HandlingObject):
     # -------------------------------------------------------------------------
     def get_hash(self, password, schema=None):
 
-        hashed_passwd = self.passlib_context.hash(password, self.schema)
+        if not schema:
+            schema = self.schema
+
+        hashed_passwd = self.passlib_context.hash(password)
         return hashed_passwd
 
     # -------------------------------------------------------------------------