# -*- coding: utf-8 -*-
"""
+@summary: A handler module handling with LDAP passwords.
+
@author: Frank Brehm
@contact: frank.brehm@pixelpark.com
@copyright: © 2023 by Frank Brehm, Berlin
-@summary: A handler module handling with LDAP passwords.
"""
from __future__ import absolute_import
import logging
# Third party modules
-import passlib.apps
HAS_CRACKLIB = False
try:
except ImportError:
pass
-from fb_tools.common import to_str, to_bytes
-from fb_tools.handling_obj import HandlingObject
+from fb_tools.common import to_bytes
+from fb_tools.common import to_str
from fb_tools.errors import FbHandlerError
+from fb_tools.handling_obj import HandlingObject
+
+import passlib.apps
# Own modules
from ..xlate import XLATOR
_ = XLATOR.gettext
ngettext = XLATOR.ngettext
-__version__ = '0.3.3'
+__version__ = '0.3.4'
# =============================================================================
class LdapPwdHandlerError(FbHandlerError):
"""Exception class for all exceptions in this handler module."""
+
pass
# -------------------------------------------------------------------------
def __init__(self, schema):
-
+ """Initialize the WrongPwdSchemaError object."""
self.schema = schema
# -------------------------------------------------------------------------
def __str__(self):
"""Typecast into a string."""
- return _("Encryption schema {!r} not found.").format(self.schema)
+ return _('Encryption schema {!r} not found.').format(self.schema)
# =============================================================================
class WrongSaltError(FbHandlerError):
"""Exception class in case of a wrong salt."""
+
pass
# =============================================================================
class WrongRoundsError(FbHandlerError):
"""Exception class in case of a wrong calculation rounds."""
+
pass
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_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_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(
+ 'A hashing method derived from {} with additional computing rounds.').format(
'SHA-512'),
}
# -------------------------------------------------------------------------
@classmethod
def init_pass_schemes(cls):
-
+ """Initialize passlib contexts for possible schemes."""
cls.available_schemes = []
all_handlers = passlib.registry.list_crypt_handlers()
self, appname=None, verbose=0, version=__version__, base_dir=None,
simulate=False, force=False, assumed_answer=None,
terminal_has_colors=False, initialized=False):
-
+ """Initialize the LdapPasswordHandler object."""
self.schema = self.default_schema
self.schema_id = self.default_schema_id
# -------------------------------------------------------------------------
@property
def salt_info(self):
- """Gives information about possible salt of the current schema."""
+ """Give information about possible salt of the current schema."""
if not hasattr(self.__class__, 'passlib_context'):
return None
# -------------------------------------------------------------------------
@property
def rounds_info(self):
- """Gives information about possible rounds parameter of the current schema."""
+ """Give information about possible rounds parameter of the current schema."""
if not hasattr(self.__class__, 'passlib_context'):
return None
# -------------------------------------------------------------------------
def as_dict(self, short=True):
"""
- Transforms the elements of the object into a dict
+ Transform the elements of the object into a dict.
@param short: don't include local properties in resulting dict.
@type short: bool
@return: structure as dict
@rtype: dict
"""
-
res = super(LdapPasswordHandler, self).as_dict(short=short)
default_schema = self.passlib_context.default_scheme()
def update_schema(
cls, schema, rounds=None, default_rounds=None, vary_rounds=None,
min_rounds=None, max_rounds=None):
-
+ """Update the context data of the given schema."""
if schema not in cls.available_schemes:
- msg = _("Invalid schema {!r} given for update.").format(schema)
+ msg = _('Invalid schema {!r} given for update.').format(schema)
raise LdapPwdHandlerError(msg)
update_args = {}
if rounds:
- key = "{}__rounds".format(schema)
+ key = '{}__rounds'.format(schema)
update_args[key] = rounds
if default_rounds:
- key = "{}__default_rounds".format(schema)
+ key = '{}__default_rounds'.format(schema)
update_args[key] = default_rounds
if vary_rounds:
- key = "{}__vary_rounds".format(schema)
+ key = '{}__vary_rounds'.format(schema)
update_args[key] = vary_rounds
if min_rounds:
- key = "{}__min_rounds".format(schema)
+ key = '{}__min_rounds'.format(schema)
update_args[key] = min_rounds
if max_rounds:
- key = "{}__max_rounds".format(schema)
+ key = '{}__max_rounds'.format(schema)
update_args[key] = max_rounds
if update_args:
# -------------------------------------------------------------------------
def show_hashing_schemes(self):
-
+ """Show all possible hashing schemes on STDOUT."""
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:")
+ title = _('Usable Hashing schemes:')
print(title)
print('-' * len(title))
print()
desc = self.schema_description[method]
if 'pbkdf2' in method:
desc += ' ' + _(
- "This schema cannot be used for authentication on a "
- "current freeradius server.")
+ 'This schema cannot be used for authentication on a '
+ 'current freeradius server.')
if method == self.schema:
- desc += ' ' + _("This is the default schema.")
+ desc += ' ' + _('This is the default schema.')
line = ' * {id:<{max_len}} - '.format(id=schema_id, max_len=max_len_schema)
line += desc
# -------------------------------------------------------------------------
def set_schema(self, schema):
-
+ """Set the current hashing schema tho the given schema name."""
if schema not in self.available_schemes:
raise WrongPwdSchemaError(schema)
# -------------------------------------------------------------------------
def set_schema_by_id(self, given_schema_id):
-
+ """Set the current hashing schema tho the given schema Id."""
found = False
for schema in self.available_schemes:
schema_id = self.schema_ids[schema]
- LOG.debug("Testing for {m!r} ({s}) ...".format(m=schema, s=schema_id))
+ LOG.debug('Testing for {m!r} ({s}) ...'.format(m=schema, s=schema_id))
if schema_id == given_schema_id:
self.passlib_context.update(default=schema)
self.schema = schema
# -------------------------------------------------------------------------
def verify_salt(self, salt, schema=None):
-
+ """Verify validity of the given salt depending on the used schema."""
if not schema:
schema = self.schema
handler = self.passlib_context.handler(schema)
if 'salt' not in handler.setting_kwds:
- msg = _("The password schema {!r} does not support a password salt.").format(schema)
+ msg = _('The password schema {!r} does not support a password salt.').format(schema)
raise WrongSaltError(msg)
if len(salt) < handler.min_salt_size:
- msg = _("The password salt must be at least by {} characters.").format(
+ msg = _('The password salt must be at least by {} characters.').format(
handler.min_salt_size)
raise WrongSaltError(msg)
if len(salt) > handler.max_salt_size:
- msg = _("The password salt may have a length of maximum {} characters.").format(
+ msg = _('The password salt may have a length of maximum {} characters.').format(
handler.max_salt_size)
raise WrongSaltError(msg)
if self.verbose > 1:
- LOG.debug("Usable characters: {!r}".format(handler.salt_chars))
+ LOG.debug('Usable characters: {!r}'.format(handler.salt_chars))
if isinstance(handler.salt_chars, (bytes, bytearray)):
salt = to_bytes(salt)
for character in salt:
if character not in handler.salt_chars:
- msg = _("Found invalid character {!r} in password salt.").format(character)
+ msg = _('Found invalid character {!r} in password salt.').format(character)
raise WrongSaltError(msg)
return salt
# ------------------------------------------------------------------------
def verify_rounds(self, rounds, schema=None):
-
+ """Verify validity of the given calculation rounds depending on the used schema."""
if not schema:
schema = self.schema
handler = self.passlib_context.handler(schema)
if 'rounds' not in handler.setting_kwds:
- msg = _("The password schema {!r} does not support calculation rounds.").format(schema)
+ msg = _('The password schema {!r} does not support calculation rounds.').format(schema)
raise WrongRoundsError(msg)
try:
rounds = int(rounds)
except (TypeError, ValueError) as e:
- msg = _("Wrong value {v!r} for calculation rounds: {e}").format(v=rounds, e=e)
+ msg = _('Wrong value {v!r} for calculation rounds: {e}').format(v=rounds, e=e)
raise WrongRoundsError(msg)
if rounds < handler.min_rounds:
- msg = _("The value for the calculation rounds has to be at least {}.").format(
+ msg = _('The value for the calculation rounds has to be at least {}.').format(
handler.min_rounds)
raise WrongRoundsError(msg)
if rounds > handler.max_rounds:
- msg = _("The value for the calculation rounds has to at most {}.").format(
+ msg = _('The value for the calculation rounds has to at most {}.').format(
handler.max_rounds)
raise WrongRoundsError(msg)
# -------------------------------------------------------------------------
def get_hash(self, password, schema=None, salt=None, rounds=None):
-
+ """Generate the hash of the given password."""
if not schema:
schema = self.schema
# -------------------------------------------------------------------------
def check_password_quality(self, password):
-
+ """Check the quality of the given password, if the cracklib module is available."""
if not HAS_CRACKLIB:
msg = _(
- "Cannot testing the quality of the new password, because the "
- "Python module {!r} is not installed.").format('cracklib')
+ 'Cannot testing the quality of the new password, because the '
+ 'Python module {!r} is not installed.').format('cracklib')
LOG.warn(msg)
return True
- LOG.info(_("Testing quality of new password ..."))
+ LOG.info(_('Testing quality of new password ...'))
try:
cracklib.VeryFascistCheck(password)
except ValueError as e:
- msg = _("Quality of the new password is not sufficient:") + ' ' + str(e)
+ msg = _('Quality of the new password is not sufficient:') + ' ' + str(e)
LOG.error(msg)
return False
- LOG.debug("The quality of the new password seems to be sufficient.")
+ LOG.debug('The quality of the new password seems to be sufficient.')
return True
+# =============================================================================
+if __name__ == '__main__':
+
+ pass
+
+# =============================================================================
+
# vim: ts=4 et list