gvafile/gvafile/fileservertasks/tasks.py

358 lines
12 KiB
Python

"""
This module defines `Celery`_ tasks to manage file system entities.
.. _Celery: http://www.celeryproject.org/
"""
from __future__ import absolute_import, unicode_literals
import os
import subprocess
from tempfile import mkstemp
from gvafile import settings
from celery import shared_task
from celery.utils.log import get_task_logger
_LOGGER = get_task_logger(__name__)
SUDO_CMD = '/usr/bin/sudo'
INSTALL_CMD = '/usr/bin/install'
SETFACL_CMD = '/usr/bin/setfacl'
RM_CMD = '/bin/rm'
def log_and_raise(exception, message, *args):
"""
Log and raise a :py:class:`subprocess.CalledProcessError`.
:param exception: exception
:param str message: log message
:param args: arguments to fill placeholders in message
:raises Exception: raises an exception with the formatted message
"""
logargs = list(args) + [exception.returncode, exception.output]
_LOGGER.error(message + "\nreturncode: %d\noutput:\n%s", *logargs)
raise Exception(message % args)
def _build_authorized_keys_path(username):
"""
Constructs the file path for the authorized_keys file for a given username.
:param str username: the user name
:return: the file name
:rtype: str
"""
return os.path.join(
settings.GVAFILE_SFTP_AUTHKEYS_DIRECTORY, username, 'keys')
def _build_sftp_directory_name(username):
"""
Constructs the SFTP directory name for a given username.
"""
return os.path.join(settings.GVAFILE_SFTP_DIRECTORY, username)
def _build_mail_directory_name(username):
"""
Constructs the mailbox directory name for a given username.
"""
return os.path.join(settings.GVAFILE_MAIL_DIRECTORY, username)
@shared_task
def setup_file_sftp_userdir(username):
"""
This task creates the home directory for an SFTP user if it does not exist
yet.
:param str username: the user name
:raises Exception: if the SFTP directory of the user cannot be created
:return: the created directory name
:rtype: str
"""
sftp_directory = _build_sftp_directory_name(username)
try:
subprocess.check_output([
SUDO_CMD, INSTALL_CMD, '-o', 'root', '-g', username,
'-m', '0750', '-d', sftp_directory], stderr=subprocess.STDOUT)
subprocess.check_output([
SUDO_CMD, SETFACL_CMD, '-m', 'www-data:--x',
sftp_directory], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as cpe:
log_and_raise(
cpe, 'cold not create SFTP directory for user %s', username)
_LOGGER.info(
'created sftp directory %s for user %s', sftp_directory, username)
return sftp_directory
@shared_task
def delete_file_sftp_userdir(username):
"""
This task recursively deletes the home directory of an SFTP user if it
does not exist yet.
:param str username: the user name
:raises Exception: if the SFTP directory of the user cannot be removed
:return: the removed directory name
:rtype: str
"""
sftp_directory = _build_sftp_directory_name(username)
try:
subprocess.check_output([
SUDO_CMD, RM_CMD, '-r', '-f', sftp_directory],
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as cpe:
log_and_raise(
cpe, 'could not remove SFTP directory for user %s', username)
_LOGGER.info(
"deleted sftp directory %s of user %s", sftp_directory, username)
return sftp_directory
@shared_task
def setup_file_mail_userdir(username):
"""
This task creates the mail base directory for a user if it does not exist
yet.
:param str username: the user name
:raises Exception: if the mail base directory for the user cannot be
created
:return: the created directory name
:rtype: str
"""
mail_directory = _build_mail_directory_name(username)
try:
subprocess.check_output([
SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username,
'-m', '0500', '-d', mail_directory], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as cpe:
log_and_raise(
cpe, 'could not create mail base directory for user %s', username)
_LOGGER.info(
'created mail directory %s for user %s', mail_directory, username)
return mail_directory
@shared_task
def delete_file_mail_userdir(username):
"""
This task recursively deletes the mail base directory for a user if it
does not exist yet.
:param str username: the user name
:raises Exception: if the mail base directory of the user cannot be removed
:return: the removed directory name
:rtype: str
"""
mail_directory = _build_mail_directory_name(username)
try:
subprocess.check_output([
SUDO_CMD, RM_CMD, '-r', '-f', mail_directory],
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as cpe:
log_and_raise(
cpe, 'could not remove mail base directory of user %s', username)
_LOGGER.info(
'deleted mail directory %s of user %s', mail_directory, username)
return mail_directory
@shared_task
def create_file_mailbox(username, mailboxname):
"""
This task creates a new mailbox directory for the given user and mailbox
name.
:param str username: the user name
:param str mailboxname: the mailbox name
:raises Exception: if the mailbox directory cannot be created
:return: the created mailbox directory name
:rtype: str
"""
mailbox_directory = os.path.join(
_build_mail_directory_name(username), mailboxname)
try:
subprocess.check_output([
SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username,
'-m', '0700', '-d', mailbox_directory], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as cpe:
log_and_raise(
cpe, 'could not create mailbox %s for user %s', mailboxname,
username)
_LOGGER.info(
'created mailbox directory %s for user %s', mailbox_directory,
username)
return mailbox_directory
@shared_task
def delete_file_mailbox(username, mailboxname):
"""
This task deletes the given mailbox of the given user.
:param str username: the user name
:param str mailboxname: the mailbox name
:raises Exception: if the mailbox directory cannot be deleted
:return: the deleted mailbox directory name
:rtype: str
"""
mailbox_directory = os.path.join(
_build_mail_directory_name(username), mailboxname)
try:
subprocess.check_output([
SUDO_CMD, RM_CMD, '-r', '-f', mailbox_directory],
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as cpe:
log_and_raise(
cpe, 'could not remove mailbox %s of user %s', mailboxname,
username)
_LOGGER.info(
'deleted mailbox directory %s of user %s', mailbox_directory, username)
return mailbox_directory
@shared_task
def create_file_website_hierarchy(username, sitename):
"""
This task creates the directory hierarchy for a website.
:param str username: the user name
:param str sitename: name of the website
:raises Exception: if the website directory hierarchy directory cannot be
created
:return: the directory name
:rtype: str
"""
website_directory = os.path.join(
_build_sftp_directory_name(username), sitename)
try:
subprocess.check_output([
SUDO_CMD, INSTALL_CMD, '-o', 'root', '-g', username,
'-m', '0750', '-d', website_directory], stderr=subprocess.STDOUT)
subprocess.check_output([
SUDO_CMD, SETFACL_CMD, '-m', 'www-data:--x',
website_directory], stderr=subprocess.STDOUT)
htmldir = os.path.join(website_directory, 'html')
subprocess.check_output([
SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username,
'-m', '0750', '-d', htmldir], stderr=subprocess.STDOUT)
subprocess.check_output([
SUDO_CMD, SETFACL_CMD, '-m', 'www-data:r-x',
htmldir], stderr=subprocess.STDOUT)
subprocess.check_output([
SUDO_CMD, SETFACL_CMD, '-d', '-m', 'www-data:r-X',
htmldir], stderr=subprocess.STDOUT)
tmpdir = os.path.join(website_directory, 'tmp')
subprocess.check_output([
SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username,
'-m', '0750', '-d', tmpdir], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as cpe:
log_and_raise(
cpe,
("could not setup website file hierarchy for user %s's "
"website %s"), username, sitename)
_LOGGER.info(
'created website directory %s for user %s', website_directory,
username)
return website_directory
@shared_task
def delete_file_website_hierarchy(username, sitename):
"""
This task deletes the website hierarchy recursively.
:param str username: the user name
:param str sitename: name of the website
:raises Exception: if the website directory hierarchy directory cannot be
deleted
:return: the directory name
:rtype: str
"""
website_directory = os.path.join(
_build_sftp_directory_name(username), sitename)
try:
subprocess.check_output([
SUDO_CMD, RM_CMD, '-r', '-f', website_directory],
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as cpe:
log_and_raise(
cpe,
("could not delete the website file hierarchy of user %s's "
"website %s"), username, sitename)
_LOGGER.info(
'deleted website directory %s of user %s', website_directory, username)
return website_directory
@shared_task
def set_file_ssh_authorized_keys(username, ssh_keys):
"""
This task sets the authorized keys for ssh logins.
:param str username: the user name
:param list ssh_key: an ssh_key
:raises Exception: if the update of the creation or update of ssh
authorized_keys failed
:return: the name of the authorized_keys file
:rtype: str
"""
ssh_authorized_keys_file = _build_authorized_keys_path(username)
if ssh_keys:
try:
authkeystemp, filename = mkstemp()
conffile = os.fdopen(authkeystemp, 'w')
conffile.write("\n".join(ssh_keys))
finally:
if conffile:
conffile.close()
try:
subprocess.check_output([
SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username,
'-m', '0500', '-d', os.path.dirname(ssh_authorized_keys_file)],
stderr=subprocess.STDOUT)
subprocess.check_output([
SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username,
'-m', '0400', filename, ssh_authorized_keys_file],
stderr=subprocess.STDOUT)
subprocess.check_output([
SUDO_CMD, RM_CMD, filename], stderr=subprocess.STDOUT)
_LOGGER.info(
'set %d authorized_keys for user %s', len(ssh_keys), username)
except subprocess.CalledProcessError as cpe:
log_and_raise(
cpe, 'could not write authorized_keys file for user %s',
username)
else:
try:
subprocess.check_output([
SUDO_CMD, RM_CMD, '-rf',
os.path.dirname(ssh_authorized_keys_file)],
stderr=subprocess.STDOUT)
_LOGGER.info(
'deleted authorized_keys of user %s', username)
except subprocess.CalledProcessError as cpe:
log_and_raise(
cpe, 'could not remove the authorized_keys file of user %s',
username)
return ssh_authorized_keys_file