gvaldap/gvaldap/ldaptasks/tasks.py
Jan Dittberner 34f788e099 Reorganize package structure
This commit reorganizes the package structure. The gvaldap.settings
modules have been merged. The gvaldap.ldaptasks module has been move up
one level to have task names without the gvaldap prefix.

isort control instructions have been added to setup.cfg.
2020-03-03 12:20:13 +01:00

364 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 celery import shared_task
from celery.exceptions import Reject
from celery.utils.log import get_task_logger
from django.core.exceptions import ObjectDoesNotExist
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