# -*- coding: utf-8 -*-
"""
+@summary: An application module for disabling or removing a user from LDAP.
+
@author: Frank Brehm
@contact: frank.brehm@pixelpark.com
@copyright: © 2023 by Frank Brehm, Berlin
-@summary: An application module for disabling or removing a user from LDAP
"""
from __future__ import absolute_import
# Standard modules
+import crypt
import logging
import time
-import crypt
# Third party modules
-from ldap3 import MODIFY_REPLACE, MODIFY_ADD, MODIFY_DELETE
-
-# Own modules
from fb_tools.collections import CIStringSet
-from fb_tools.common import to_bool, is_sequence
+from fb_tools.common import is_sequence, to_bool
from fb_tools.xlate import format_list
-from .. import pp
-
-from ..xlate import XLATOR
+from ldap3 import MODIFY_ADD, MODIFY_DELETE, MODIFY_REPLACE
-from .ldap import LdapAppError, FatalLDAPError
+# Own modules
from .ldap import BaseLdapApplication
+from .ldap import FatalLDAPError, LdapAppError
+from .. import pp
+from ..xlate import XLATOR
-__version__ = '0.6.0'
+__version__ = '0.6.1'
LOG = logging.getLogger(__name__)
_ = XLATOR.gettext
show_simulate_option = True
- default_nologin_shell = "/usr/sbin/nologin"
+ default_nologin_shell = '/usr/sbin/nologin'
value_inactive = 'inactive'
raw_empty_passwd = 'none'
# -------------------------------------------------------------------------
def __init__(self, appname=None, base_dir=None):
-
+ """Initialize the RemoveLdapUserApplication object."""
self.use_default_ldap_connection = False
self.use_multiple_ldap_connections = True
self.show_cmdline_ldap_timeout = True
self.shadow_expire = int(time.time() / 3600 / 24) - 100
desc = _(
- "Disables or removes the given users from LDAP. "
- "If disabling, then the user will not be really removed, but disabled "
- "by locking the password, setting all status flags to {inact!r}, "
- "assigning {shell!r} as login shell und removing the user from all groups. "
- "When removing (or purging) the user will be really removed from LDAP.")
+ 'Disables or removes the given users from LDAP. '
+ 'If disabling, then the user will not be really removed, but disabled '
+ 'by locking the password, setting all status flags to {inact!r}, '
+ 'assigning {shell!r} as login shell und removing the user from all groups. '
+ 'When removing (or purging) the user will be really removed from LDAP.')
desc = desc.format(inact=self.value_inactive, shell=self.nologin_shell)
super(RemoveLdapUserApplication, self).__init__(
# -------------------------------------------
@property
def deactivate(self):
- """Defines, that the given users will not be removed, bur deactivated instaed."""
+ """Define, that the given users will not be removed, bur deactivated instaed."""
return self._deactivate
@deactivate.setter
# -------------------------------------------------------------------------
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(RemoveLdapUserApplication, self).as_dict(short=short)
res['deactivate'] = self.deactivate
# -------------------------------------------------------------------------
def init_arg_parser(self):
-
+ """Initialize specific command line parameters for this application."""
remove_group = self.arg_parser.add_argument_group(_('Removing options'))
remove_mode_group = remove_group.add_mutually_exclusive_group()
- help_default = ' ' + _("This is the default.")
- help_mutual = " " + _("This is mutually exclusive to {!r}.")
+ help_default = ' ' + _('This is the default.')
+ help_mutual = ' ' + _('This is mutually exclusive to {!r}.')
- help_txt = _("Deactivating the user instead of removing it.")
+ help_txt = _('Deactivating the user instead of removing it.')
help_txt += help_mutual.format('--remove')
if self.deactivate:
help_txt += help_default
remove_mode_group.add_argument(
- '-d', '--deactivate', dest="deactivate", action='store_true', help=help_txt)
+ '-d', '--deactivate', dest='deactivate', action='store_true', help=help_txt)
- help_txt = _("Removing the user from LDAP.")
+ help_txt = _('Removing the user from LDAP.')
help_txt += help_mutual.format('--deactivate')
if not self.deactivate:
help_txt += help_default
remove_mode_group.add_argument(
- '-R', '--remove', dest="remove", action='store_true', help=help_txt)
+ '-R', '--remove', dest='remove', action='store_true', help=help_txt)
help_txt = _(
"Don't remove all mail addresses of the users to remove from all "
- "mailing lists.")
+ 'mailing lists.')
remove_group.add_argument(
'-N', '--no-del-mail-addresses-from-lists', dest='no_del_mails',
action='store_true', help=help_txt)
remove_group.add_argument(
'users', nargs='*', metavar=_('USER'),
help=_(
- "The user, which should be deactivated or removed. "
- "They may be given by their Uid (the alphanumeric POSIX name), "
- "their mail address or their LDAP DN (be aware, that this may be "
- "different in the particular LDAP instances).")
+ 'The user, which should be deactivated or removed. '
+ 'They may be given by their Uid (the alphanumeric POSIX name), '
+ 'their mail address or their LDAP DN (be aware, that this may be '
+ 'different in the particular LDAP instances).')
)
super(RemoveLdapUserApplication, self).init_arg_parser()
# -------------------------------------------------------------------------
def post_init(self):
- """
- Method to execute before calling run().
- """
-
+ """Execute isome steps before calling run()."""
super(RemoveLdapUserApplication, self).post_init()
given_users = getattr(self.args, 'users', [])
if not given_users:
- LOG.error(_("No users to remove given."))
+ LOG.error(_('No users to remove given.'))
self.exit(1)
# deactivate = bool(getattr(self.args, 'remove', False))
# -------------------------------------------------------------------------
def check_instances(self):
- """Checking given instances for admin and read/write access."""
-
- msg = _("Checking given instances for admin and read/write access.")
+ """Check given instances for admin and read/write access."""
+ msg = _('Checking given instances for admin and read/write access.')
LOG.debug(msg)
all_ok = True
for inst_name in self.ldap_instances:
if inst_name not in self.cfg.ldap_connection:
- msg = _("LDAP instance {!r} not found in configuration.").format(inst_name)
+ msg = _('LDAP instance {!r} not found in configuration.').format(inst_name)
LOG.error(msg)
all_ok = False
continue
inst = self.cfg.ldap_connection[inst_name]
if inst.readonly:
- msg = _("LDAP instance {!r} has only readonly access.").format(inst_name)
+ msg = _('LDAP instance {!r} has only readonly access.').format(inst_name)
LOG.error(msg)
all_ok = False
if not inst.is_admin:
- msg = _("No admin access to LDAP instance {!r}.").format(inst_name)
+ msg = _('No admin access to LDAP instance {!r}.').format(inst_name)
LOG.error(msg)
all_ok = False
self.exit(5)
if not self.dns:
- msg = _("All given users were not found in any LDAP instance.")
+ msg = _('All given users were not found in any LDAP instance.')
LOG.warn(msg)
self.exit(1)
if self.verbose > 1:
- msg = _("Evaluated DNs to remove:")
+ msg = _('Evaluated DNs to remove:')
LOG.debug(msg + '\n' + pp(self.dns))
if not self.request_for_remove():
if not self.quiet:
print()
if self.deactivate:
- msg = _("Start disabling user entries in:")
+ msg = _('Start disabling user entries in:')
else:
- msg = _("Start removing user entries in:")
+ msg = _('Start removing user entries in:')
self.countdown(number=3, delay=1, prompt=msg)
else:
if self.deactivate:
- msg = _("Start disabling user entries ...")
+ msg = _('Start disabling user entries ...')
else:
- msg = _("Start removing user entries ...")
+ msg = _('Start removing user entries ...')
LOG.warn(msg)
for inst in self.dns:
# -------------------------------------------------------------------------
def request_for_remove(self):
-
+ """Ask the operator for really removing the given user(s)."""
if self.quiet:
return True
print()
if self.deactivate:
- msg = _("Do you really want to deactivate the following users?")
+ msg = _('Do you really want to deactivate the following users?')
else:
- msg = _("Do you really want to remove the following users?")
+ msg = _('Do you really want to remove the following users?')
print(self.colored(msg, 'CYAN'))
print(self.colored(('-' * len(msg)), 'CYAN'))
print()
for dn in self.dns[inst]:
if not first:
uri = ' '
- msg = " {a} {uri:<{max_len}} - {dn}".format(
+ msg = ' {a} {uri:<{max_len}} - {dn}'.format(
a=self.colored('*', 'CYAN'), uri=uri, max_len=max_len,
dn=self.colored(dn, 'CYAN'))
print(msg)
print()
if self.deactivate:
- msg = _("Deactivate [{yes}/{no}]?")
+ msg = _('Deactivate [{yes}/{no}]?')
else:
- msg = _("Remove [{yes}/{no}]?")
+ msg = _('Remove [{yes}/{no}]?')
msg = msg.format(
yes=self.colored(_('yes'), 'RED'), no=self.colored(_('No'), 'GREEN')) + ' '
return self.ask_for_yes_or_no(msg)
# -------------------------------------------------------------------------
def eval_user_dns(self, user):
-
- LOG.debug(_("Evaluating DNs of the user {!r} to remove ...").format(user))
+ """Try to evaluate the DNs of the users to remove by their UID, DN or mail address."""
+ LOG.debug(_('Evaluating DNs of the user {!r} to remove ...').format(user))
usr = user.strip()
if usr == '':
- msg = _("Empty user given.")
+ msg = _('Empty user given.')
LOG.warn(msg)
self.wrong_users = True
return
dns = self.get_user_dn(usr, inst)
if dns:
if self.verbose > 2:
- msg = _("Got DN {dn!r} for user {user!r} in LDAP instance {inst}.").format(
+ msg = _('Got DN {dn!r} for user {user!r} in LDAP instance {inst}.').format(
dn=dns, user=usr, inst=connect_info.url)
LOG.debug(msg)
if inst not in self.dns:
self.dns[inst] = []
if is_sequence(dns):
if len(dns) > 1:
- msg = _("Found {nr} entries for user {u!r} in LDAP instance {i}.").format(
+ msg = _('Found {nr} entries for user {u!r} in LDAP instance {i}.').format(
nr=len(dns), u=usr, i=connect_info.url)
LOG.warn(msg)
self.wrong_users = True
if dns not in self.dns[inst]:
self.dns[inst].append(dns)
else:
- msg = _("Did not found user {user!r} in LDAP instance {inst}.").format(
+ msg = _('Did not found user {user!r} in LDAP instance {inst}.').format(
user=usr, inst=connect_info.url)
LOG.info(msg)
# -------------------------------------------------------------------------
def remove_users_from_inst(self, inst):
-
+ """Remove finally all given users from LDAP."""
connect_info = self.cfg.ldap_connection[inst]
if self.deactivate:
- msg = _("Deactivating all given users from {} ...").format(connect_info.url)
+ msg = _('Deactivating all given users from {} ...').format(connect_info.url)
else:
- msg = _("Removing all given users from {} ...").format(connect_info.url)
+ msg = _('Removing all given users from {} ...').format(connect_info.url)
LOG.info(msg)
for dn in self.dns[inst]:
# -------------------------------------------------------------------------
def remove_user(self, inst, dn):
-
+ """Remove the given user from LDAP or deactivate it."""
connect_info = self.cfg.ldap_connection[inst]
if self.deactivate:
- msg = _("Deactivating user {dn!r} from {inst} ...").format(
+ msg = _('Deactivating user {dn!r} from {inst} ...').format(
dn=dn, inst=connect_info.url)
else:
- msg = _("Removing user {dn!r} from {inst} ...").format(dn=dn, inst=connect_info.url)
+ msg = _('Removing user {dn!r} from {inst} ...').format(dn=dn, inst=connect_info.url)
LOG.info(msg)
entry = self.get_entry(dn, inst)
attributes = self.normalized_attributes(entry)
if self.verbose > 1:
- msg = _("Attributes of {!r}:").format(dn)
+ msg = _('Attributes of {!r}:').format(dn)
LOG.debug(msg + '\n' + pp(attributes.as_dict()))
if not self.setting_user_status(inst, dn, attributes):
try:
self.delete_entry(inst, dn)
except FatalLDAPError as e:
- msg = _("{c} on removing user {dn!r}: {e}").format(
+ msg = _('{c} on removing user {dn!r}: {e}').format(
c=e.__class__.__name__, dn=dn, e=e)
LOG.error(msg)
return False
if self.deactivate:
- msg = _("User {dn!r} successful deactivated on {inst}.").format(
+ msg = _('User {dn!r} successful deactivated on {inst}.').format(
dn=dn, inst=connect_info.url)
else:
- msg = _("User {dn!r} successful removed from {inst}.").format(
+ msg = _('User {dn!r} successful removed from {inst}.').format(
dn=dn, inst=connect_info.url)
LOG.info(msg)
# -------------------------------------------------------------------------
def setting_user_status(self, inst, dn, attributes):
-
+ """Set the user status of the given user in LDAP."""
connect_info = self.cfg.ldap_connection[inst]
changes = {}
if 'posixAccount' in attributes['objectClass']:
changes['loginShell'] = [(MODIFY_REPLACE, self.nologin_shell)]
- LOG.info(_("Updating user info for {dn!r} on {inst} ...").format(
+ LOG.info(_('Updating user info for {dn!r} on {inst} ...').format(
dn=dn, inst=connect_info.url))
try:
self.modify_entry(inst, dn, changes)
except FatalLDAPError as e:
- msg = _("{c} on deactivating user {dn!r}: {e}").format(
+ msg = _('{c} on deactivating user {dn!r}: {e}').format(
c=e.__class__.__name__, dn=dn, e=e)
msg += '\n' + _('Changes:') + '\n' + pp(changes)
LOG.error(msg)
# -------------------------------------------------------------------------
def remove_all_memberships(self, inst, dn):
-
+ """Remove the given DN from all LDAP groups as a member."""
connect_info = self.cfg.ldap_connection[inst]
group_dns = self.get_group_memberships(inst, dn)
if not group_dns:
- msg = _("Did not found any group memberships of {dn!r} in {inst}.").format(
+ msg = _('Did not found any group memberships of {dn!r} in {inst}.').format(
dn=dn, inst=connect_info.url)
LOG.debug(msg)
return True
for group_dn in group_dns:
- LOG.info(_("Removing user {u!r} from group {g!r} ...").format(u=dn, g=group_dn))
+ LOG.info(_('Removing user {u!r} from group {g!r} ...').format(u=dn, g=group_dn))
changes = {'member': [(MODIFY_DELETE, dn)], }
try:
self.modify_entry(inst, group_dn, changes)
except FatalLDAPError as e:
- msg = _("{c} on removing user {dn!r} from group {g!r}: {e}").format(
+ msg = _('{c} on removing user {dn!r} from group {g!r}: {e}').format(
c=e.__class__.__name__, dn=dn, g=group_dn, e=e)
msg += '\n' + _('Changes:') + '\n' + pp(changes)
LOG.error(msg)
# -------------------------------------------------------------------------
def remove_all_unique_memberships(self, inst, dn):
-
+ """Remove the given DN from all LDAP groups as a uniqueMember."""
connect_info = self.cfg.ldap_connection[inst]
group_dns = self.get_unique_group_memberships(inst, dn)
if not group_dns:
- msg = _("Did not found any unique group memberships of {dn!r} in {inst}.").format(
+ msg = _('Did not found any unique group memberships of {dn!r} in {inst}.').format(
dn=dn, inst=connect_info.url)
LOG.debug(msg)
return True
for group_dn in group_dns:
- LOG.info(_("Removing user {u!r} from group {g!r} ...").format(u=dn, g=group_dn))
+ LOG.info(_('Removing user {u!r} from group {g!r} ...').format(u=dn, g=group_dn))
changes = {'uniqueMember': [(MODIFY_DELETE, dn)], }
try:
self.modify_entry(inst, group_dn, changes)
except FatalLDAPError as e:
- msg = _("{c} on removing user {dn!r} from group {g!r}: {e}").format(
+ msg = _('{c} on removing user {dn!r} from group {g!r}: {e}').format(
c=e.__class__.__name__, dn=dn, g=group_dn, e=e)
msg += '\n' + _('Changes:') + '\n' + pp(changes)
LOG.error(msg)
# -------------------------------------------------------------------------
def remove_all_posixgroup_memberships(self, inst, uid):
-
+ """Remove the given UID from all legacy POSIX groups."""
connect_info = self.cfg.ldap_connection[inst]
- msg = _("Deleting user {uid!r} from all POSIX groups in {inst}.").format(
+ msg = _('Deleting user {uid!r} from all POSIX groups in {inst}.').format(
uid=uid, inst=connect_info.url)
LOG.debug(msg)
group_dns = self.get_posix_group_memberships(inst, uid)
if not group_dns:
- msg = _("Did not found any POSIX group memberships of {uid!r} in {inst}.").format(
+ msg = _('Did not found any POSIX group memberships of {uid!r} in {inst}.').format(
uid=uid, inst=connect_info.url)
LOG.debug(msg)
return True
for group_dn in group_dns:
- LOG.info(_("Removing user {u!r} from group {g!r} ...").format(u=uid, g=group_dn))
+ LOG.info(_('Removing user {u!r} from group {g!r} ...').format(u=uid, g=group_dn))
changes = {'memberUid': [(MODIFY_DELETE, uid)], }
try:
self.modify_entry(inst, group_dn, changes)
except FatalLDAPError as e:
- msg = _("{c} on removing user {dn!r} from group {g!r}: {e}").format(
+ msg = _('{c} on removing user {dn!r} from group {g!r}: {e}').format(
c=e.__class__.__name__, dn=uid, g=group_dn, e=e)
msg += '\n' + _('Changes:') + '\n' + pp(changes)
LOG.error(msg)
# -------------------------------------------------------------------------
def remove_all_sudogroup_memberships(self, inst, uid):
-
+ """Remove the given UID from all sudo-Roles."""
connect_info = self.cfg.ldap_connection[inst]
- msg = _("Deleting user {uid!r} from all sudo groups in {inst}.").format(
+ msg = _('Deleting user {uid!r} from all sudo groups in {inst}.').format(
uid=uid, inst=connect_info.url)
LOG.debug(msg)
group_dns = self.get_sudo_group_memberships(inst, uid)
if not group_dns:
- msg = _("Did not found any sudo group memberships of {uid!r} in {inst}.").format(
+ msg = _('Did not found any sudo group memberships of {uid!r} in {inst}.').format(
uid=uid, inst=connect_info.url)
LOG.debug(msg)
return True
for group_dn in group_dns:
- LOG.info(_("Removing user {u!r} from group {g!r} ...").format(u=uid, g=group_dn))
+ LOG.info(_('Removing user {u!r} from group {g!r} ...').format(u=uid, g=group_dn))
changes = {'sudoUser': [(MODIFY_DELETE, uid)], }
try:
self.modify_entry(inst, group_dn, changes)
except FatalLDAPError as e:
- msg = _("{c} on removing user {dn!r} from group {g!r}: {e}").format(
+ msg = _('{c} on removing user {dn!r} from group {g!r}: {e}').format(
c=e.__class__.__name__, dn=uid, g=group_dn, e=e)
msg += '\n' + _('Changes:') + '\n' + pp(changes)
LOG.error(msg)
# -------------------------------------------------------------------------
def del_mails_from_lists(self, inst, dn, attributes):
"""Remove mail addresses of users from all mailing lists."""
- connect_info = self.cfg.ldap_connection[inst]
+ connect_info = self.cfg.ldap_connection[inst] # noqa
mail_addresses = CIStringSet()
for attr in attributes.keys():
mail_addresses.add(values)
if not len(mail_addresses):
- msg = _("No mail addresses found to remove for user {!r}.").format(dn)
+ msg = _('No mail addresses found to remove for user {!r}.').format(dn)
LOG.debug(msg)
return
# =============================================================================
-if __name__ == "__main__":
+if __name__ == '__main__':
pass