gvaldap/gvaldap/gvaldap/ldaptasks/tasks.py

358 lines
12 KiB
Python

"""
This module defines `Celery`_ tasks to manage LDAP entities.
.. _Celery: http://www.celeryproject.org/
"""
from __future__ import absolute_import
from copy import deepcopy
from django.core.exceptions import ObjectDoesNotExist
from celery import shared_task
from celery.utils.log import get_task_logger
from celery.exceptions import Reject
from ldapentities.models import (
LdapGroup,
LdapUser,
)
_LOGGER = get_task_logger(__name__)
@shared_task
def create_ldap_group(groupname, gid, description):
"""
This task creates an :py:class:`LDAP group <ldapentities.models.LdapGroup>`
if it does not exist yet.
If a group with the given name exists its group id and description
attributes are updated.
:param str groupname: the group name
:param int gid: the group id
:param str description: description text for the group
:return: dictionary containing groupname, gid, description and
:py:const:`group_dn` set to the distinguished name of the newly created
or existing LDAP group
:rtype: dict
"""
try:
ldapgroup = LdapGroup.objects.get(name=groupname)
_LOGGER.info(
'LDAP group %s with groupname %s already exists',
ldapgroup.dn, groupname)
ldapgroup.gid = gid
except LdapGroup.DoesNotExist:
ldapgroup = LdapGroup(gid=gid, name=groupname)
_LOGGER.info('created LDAP group %s', ldapgroup.dn)
ldapgroup.description = description
ldapgroup.save()
_LOGGER.info('set description of LDAP group %s', ldapgroup.dn)
return {
'groupname': groupname,
'gid': gid,
'description': description,
'group_dn': ldapgroup.dn,
}
@shared_task
def create_ldap_user(username, uid, gid, gecos, homedir, shell, password):
"""
This task creates an :py:class:`LDAP user <ldapentities.models.LdapUser>`
if it does not exist yet.
The task is rejected if the primary group of the user is not defined.
The user's fields are updated if the user already exists.
:param str username: the user name
:param int uid: the user id
:param int gid: the user's primary group's id
:param str gecos: the text for the GECOS field
:param str homedir: the user's home directory
:param str shell: the user's login shell
:param str or None password: the clear text password, if :py:const:`None`
is passed the password is not touched
:raises celery.exceptions.Reject: if the specified primary group does not
exist
:return: dictionary containing username, uid, gid, gecos, homedir, shell,
password and :py:const:`user_dn` set to the distinguished name of the
newly created or existing LDAP user
:rtype: dict
"""
try:
ldapuser = LdapUser.objects.get(username=username)
_LOGGER.info(
'LDAP user %s with username %s already exists',
ldapuser.dn, username)
except LdapUser.DoesNotExist:
ldapuser = LdapUser(username=username)
try:
ldapgroup = LdapGroup.objects.get(gid=gid)
except ObjectDoesNotExist as exc:
_LOGGER.error('LDAP group with gid %d does not exist', gid)
raise Reject(exc, requeue=False)
ldapuser.uid = uid
ldapuser.group = gid
ldapuser.gecos = gecos
ldapuser.home_directory = homedir
ldapuser.login_shell = shell
ldapuser.username = username
ldapuser.common_name = username
if password is not None:
ldapuser.set_password(password)
_LOGGER.info('set password for LDAP user %s', ldapuser.dn)
ldapuser.save()
_LOGGER.info('LDAP user %s created', ldapuser.dn)
if ldapuser.username in ldapgroup.members:
_LOGGER.info(
'LDAP user %s is already member of LDAP group %s',
ldapuser.dn, ldapgroup.dn)
else:
ldapgroup.members.append(ldapuser.username)
ldapgroup.save()
_LOGGER.info(
'LDAP user %s has been added to LDAP group %s',
ldapuser.dn, ldapgroup.dn)
return {
'username': username,
'uid': uid,
'gid': gid,
'gecos': gecos,
'homedir': homedir,
'shell': shell,
'user_dn': ldapuser.dn,
}
@shared_task
def set_ldap_user_password(username, password):
"""
This task sets the password of an existing :py:class:`LDAP user
<ldapentities.models.LdapUser>`.
:param str username: the user name
:param str password: teh clear text password
:return: dictionary containing the username and a flag
:py:const:`password_set` that is set to :py:const:`True` if the
password has been set, :py:const:`False` if the user does not exist.
:rtype: dict
"""
retval = {'username': username, 'password_set': False}
try:
ldapuser = LdapUser.objects.get(username=username)
except LdapUser.DoesNotExist:
_LOGGER.info('there is no LDAP user with username %s', username)
return retval
ldapuser.set_password(password)
ldapuser.save()
_LOGGER.info("set new password for LDAP user %s", ldapuser.dn)
retval['password_set'] = True
return retval
@shared_task(bind=True)
def add_ldap_user_to_group(self, username, groupname):
"""
This task adds the specified user to the given group.
This task does nothing if the user is already member of the group.
:param str username: the user name
:param str groupname: the group name
:raises celery.exceptions.Retry: if the user does not exist yet,
:py:func:`create_ldap_user` should be called before
:return: dictionary containing the username, groupname and a flag
:py:const`added` that is as a :py:const:`True` if the user has been
added to the group otherwise to :py:const:`False`
:rtype: dict
"""
retval = {'username': username, 'groupname': groupname, 'added': False}
try:
ldapgroup = LdapGroup.objects.get(name=groupname)
ldapuser = LdapUser.objects.get(username=username)
except LdapGroup.DoesNotExist:
_LOGGER.error('LDAP group with groupname %s does not exist', groupname)
except LdapUser.DoesNotExist as exc:
_LOGGER.error('LDAP user with username %s does not exist', username)
self.retry(exc=exc, time_limit=5)
else:
if ldapuser.username not in ldapgroup.members:
ldapgroup.members.append(ldapuser.username)
ldapgroup.save()
_LOGGER.info(
'LDAP user %s has been added to LDAP group %s',
ldapuser.username, ldapgroup.dn)
else:
_LOGGER.info(
'LDAP user %s is already in LDAP group %s',
ldapuser.username, ldapgroup.dn)
retval['added'] = True
return retval
@shared_task
def remove_ldap_user_from_group(username, groupname):
"""
This task removes the given user from the given group.
:param str username: the user name
:param str groupname: the group name
:return: dictionary containing the input parameters and a flag
:py:const:`removed` that is set to :py:const:`True` if the user has
been removed, False otherwise
:rtype: dict
"""
retval = {'username': username, 'groupname': groupname, 'removed': False}
try:
ldapgroup = LdapGroup.objects.get(name=groupname)
ldapuser = LdapUser.objects.get(username=username)
except LdapGroup.DoesNotExist:
_LOGGER.error('LDAP group with groupname %s does not exist', groupname)
except LdapUser.DoesNotExist:
_LOGGER.error('LDAP user with username %s does not exist', username)
else:
if ldapuser.username in ldapgroup.members:
ldapgroup.members.remove(ldapuser.username)
_LOGGER.info(
'removed LDAP user %s from LDAP group %s',
ldapuser.dn, ldapgroup.dn)
ldapgroup.save()
retval['removed'] = True
else:
_LOGGER.info(
'LDAP user %s is not a member of LDAP group %s',
ldapuser.dn, ldapgroup.dn)
return retval
@shared_task
def delete_ldap_user(username, *args, **kwargs):
"""
This task deletes the given user.
:param str username: the user name
:return: dictionary containing the username and a flag :py:const:`deleted`
that is set to :py:const:`True` if the user has been deleted and is set
to :py:const:`False` otherwise
:rtype: dict
.. note::
This variant can only be used at the beginning of a Celery task chain
or as a standalone task.
Use :py:func:`ldaptasks.tasks.delete_ldap_user_chained` at other
positions in the task chain
"""
retval = {'username': username, 'deleted': False}
try:
ldapuser = LdapUser.objects.get(username=username)
except LdapUser.DoesNotExist:
_LOGGER.info('there is no LDAP user with username %s', username)
else:
try:
ldapgroup = LdapGroup.objects.get(gid=ldapuser.group)
except LdapGroup.DoesNotExist:
_LOGGER.info(
'LDAP group %s of LDAP user %s does not exist',
ldapuser.group, ldapuser.dn)
else:
if ldapuser.username in ldapgroup.members:
ldapgroup.members.remove(ldapuser.username)
ldapgroup.save()
_LOGGER.info(
'removed LDAP user %s from LDAP group %s',
ldapuser.dn, ldapgroup.dn)
userdn = ldapuser.dn
ldapuser.delete()
_LOGGER.info('deleted LDAP user %s', userdn)
retval['deleted'] = True
return retval
@shared_task
def delete_ldap_user_chained(previous_result, *args, **kwargs):
"""
This task deletes the given user.
:param dict previous_result: a dictionary describing the result of the
previous step in the Celery task chain. This dictionary must contain a
:py:const:`username` key
:return: a copy of the :py:obj:`previous_result` dictionary with a new
:py:const:`deleted` key set to :py:const:`True` if the user has been
deleted and set to :py:const:`False` otherwise
:rtype: dict
"""
username = previous_result['username']
retval = deepcopy(previous_result)
retval.update(delete_ldap_user(username))
return retval
@shared_task
def delete_ldap_group_if_empty(groupname):
"""
This task deletes the given group if it is empty.
:param str groupname: the group name
:return: dictionary that contains the groupname and a flag
:py:const:`deleted` that is set to :py:const:`True` if the group has
been deleted and is set to :py:const:`False` otherwise
:rtype: dict
"""
retval = {'groupname': groupname, 'deleted': False}
try:
ldapgroup = LdapGroup.objects.get(name=groupname)
except LdapGroup.DoesNotExist:
_LOGGER.info('LDAP group with groupname %s does not exist', groupname)
else:
if len(ldapgroup.members) == 0:
groupdn = ldapgroup.dn
ldapgroup.delete()
_LOGGER.info(
'deleted LDAP group %s', groupdn)
retval['deleted'] = True
else:
_LOGGER.info(
'LDAP group %s has not been deleted. It still has %d members',
ldapgroup.dn, len(ldapgroup.members))
return retval
@shared_task
def delete_ldap_group(groupname):
"""
This task deletes the given group.
:param str groupname: the group name
:return: dictionary that contains the groupname and a flag
:py:const:`deleted` that is set to :py:const:`True` if the group has
been deleted and is set to :py:const:`False` otherwise
:rtype: dict
"""
retval = {'groupname': groupname, 'deleted': False}
try:
ldapgroup = LdapGroup.objects.get(name=groupname)
except LdapGroup.DoesNotExist:
_LOGGER.info('LDAP group with name %s does not exist', groupname)
else:
groupdn = ldapgroup.dn
ldapgroup.delete()
_LOGGER.info('deleted LDAP group %s', groupdn)
retval['deleted'] = True
return retval