From caf9d2125e74690d3ff9fff8e8a82057d91fbebd Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Thu, 25 Dec 2014 18:26:37 +0100 Subject: [PATCH 1/9] define stub tasks and implement setup_file_sftp_userdir task - create new module osusers.tasks - define helper functions _build_sftp_directory_name and _build_mail_directory_name - implement task setup_file_sftp_userdir - define stubs for delete_file_sftp_userdir, setup_file_mail_userdir and delete_file_mail_userdir --- gvafile/osusers/tasks.py | 82 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 gvafile/osusers/tasks.py diff --git a/gvafile/osusers/tasks.py b/gvafile/osusers/tasks.py new file mode 100644 index 0000000..5035169 --- /dev/null +++ b/gvafile/osusers/tasks.py @@ -0,0 +1,82 @@ +""" +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 django.conf import settings + +from celery import shared_task +from celery.utils.log import get_task_logger +from celery.exceptions import Reject + + +_logger = get_task_logger(__name__) + + +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. + + The task is rejected if the directory creation fails. + + :param str username: the user name + :raises celery.exceptions.Reject: 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', 'install', '-o', username, '-g', username, '-m', '0750', + '-d', sftp_directory], stderr=subprocess.STDOUT) + subprocess.check_output([ + 'sudo' 'setfacl', '-r', '-m', 'www-data:--x', sftp_directory], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as exc: + _logger.exception( + 'could not create SFTP directory for user {0}: {1}', + username, exc.output + ) + raise Reject(exc, requeue=False) + return sftp_directory + + +@shared_task +def delete_file_sftp_userdir(username): + pass + + +@shared_task +def setup_file_mail_userdir(username): + pass + + +@shared_task +def delete_file_mail_userdir(username): + pass From 1ba6a2ad9d081655b46f2cfedbcec02a307e263a Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Thu, 25 Dec 2014 18:32:08 +0100 Subject: [PATCH 2/9] introduce new settings GVAFILE_SFTP_DIRECTORY and GVAFILE_MAIL_DIRECTORY --- gvafile/gvafile/settings/base.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gvafile/gvafile/settings/base.py b/gvafile/gvafile/settings/base.py index 470a9cf..ccdea65 100644 --- a/gvafile/gvafile/settings/base.py +++ b/gvafile/gvafile/settings/base.py @@ -271,6 +271,12 @@ WSGI_APPLICATION = '%s.wsgi.application' % SITE_NAME ########## END WSGI CONFIGURATION +########## GVAFILE CONFIGURATION +GVAFILE_SFTP_DIRECTORY = get_env_setting('GVAFILE_SFTP_DIRECTORY') +GVAFILE_MAIL_DIRECTORY = get_env_setting('GVAFILE_MAIL_DIRECTORY') +########## END GVAFILE CONFIGURATION + + ########## CELERY CONFIGURATION BROKER_URL = get_env_setting('GVAFILE_BROKER_URL') CELERY_RESULT_BACKEND = 'amqp' From 1ad42ca4fda404d3742278535e2bf6652b394c57 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Thu, 25 Dec 2014 18:51:14 +0100 Subject: [PATCH 3/9] fix logging string formatting --- gvafile/osusers/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gvafile/osusers/tasks.py b/gvafile/osusers/tasks.py index 5035169..49b5a6c 100644 --- a/gvafile/osusers/tasks.py +++ b/gvafile/osusers/tasks.py @@ -60,7 +60,7 @@ def setup_file_sftp_userdir(username): stderr=subprocess.STDOUT) except subprocess.CalledProcessError as exc: _logger.exception( - 'could not create SFTP directory for user {0}: {1}', + 'could not create SFTP directory for user %s: %s', username, exc.output ) raise Reject(exc, requeue=False) From b2acae7dda03bc127bf608f269cb21860c2fff6a Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Thu, 25 Dec 2014 19:19:21 +0100 Subject: [PATCH 4/9] fix exception handling in setup_file_sftp_userdir --- gvafile/osusers/tasks.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/gvafile/osusers/tasks.py b/gvafile/osusers/tasks.py index 49b5a6c..34b09e4 100644 --- a/gvafile/osusers/tasks.py +++ b/gvafile/osusers/tasks.py @@ -44,8 +44,7 @@ def setup_file_sftp_userdir(username): The task is rejected if the directory creation fails. :param str username: the user name - :raises celery.exceptions.Reject: if the SFTP directory of the user cannot - be created + :raises Exception: if the SFTP directory of the user cannot be created :return: the created directory name :rtype: str @@ -58,12 +57,11 @@ def setup_file_sftp_userdir(username): subprocess.check_output([ 'sudo' 'setfacl', '-r', '-m', 'www-data:--x', sftp_directory], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as exc: + except subprocess.CalledProcessError: _logger.exception( - 'could not create SFTP directory for user %s: %s', - username, exc.output - ) - raise Reject(exc, requeue=False) + 'could not create SFTP directory for user %s', username) + raise Exception( + "could not create SFTP directory for user %s" % username) return sftp_directory From ea278eeb351defdf66eee1c00b4924e78067e09c Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Thu, 25 Dec 2014 21:20:16 +0100 Subject: [PATCH 5/9] implement and document GVAFileException --- docs/code.rst | 6 ++++++ docs/conf.py | 5 +++++ gvafile/gvafile/exceptions.py | 12 ++++++++++++ gvafile/osusers/tasks.py | 5 +++-- 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 gvafile/gvafile/exceptions.py diff --git a/docs/code.rst b/docs/code.rst index 2b273a7..227508a 100644 --- a/docs/code.rst +++ b/docs/code.rst @@ -29,6 +29,12 @@ The project module :py:mod:`gvafile` .. automodule:: gvafile.urls +:py:mod:`gvafile.exceptions` +---------------------------- + +.. automodule:: gvafile.exceptions + + :py:mod:`gvafile.wsgi` ---------------------- diff --git a/docs/conf.py b/docs/conf.py index dc4f85e..daa0c8d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -14,6 +14,7 @@ import sys import os +import django # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -22,6 +23,10 @@ sys.path.insert(0, os.path.abspath(os.path.join('..', 'gvafile'))) os.environ['GVAFILE_ALLOWED_HOSTS'] = 'localhost' os.environ['GVAFILE_SERVER_EMAIL'] = 'root@localhost' +os.environ['GVAFILE_SFTP_DIRECTORY'] = '/home/www' +os.environ['GVAFILE_MAIL_DIRECTORY'] = '/home/mail' + +django.setup() # -- General configuration ----------------------------------------------------- diff --git a/gvafile/gvafile/exceptions.py b/gvafile/gvafile/exceptions.py new file mode 100644 index 0000000..06f6cfb --- /dev/null +++ b/gvafile/gvafile/exceptions.py @@ -0,0 +1,12 @@ +""" +This module defines exceptions for gvafile. + +""" +from __future__ import unicode_literals + + +class GVAFileException(Exception): + """ + Generic Exception class for gvafile. + + """ diff --git a/gvafile/osusers/tasks.py b/gvafile/osusers/tasks.py index 34b09e4..65c8091 100644 --- a/gvafile/osusers/tasks.py +++ b/gvafile/osusers/tasks.py @@ -13,7 +13,8 @@ from django.conf import settings from celery import shared_task from celery.utils.log import get_task_logger -from celery.exceptions import Reject + +from gvafile.exceptions import GVAFileException _logger = get_task_logger(__name__) @@ -60,7 +61,7 @@ def setup_file_sftp_userdir(username): except subprocess.CalledProcessError: _logger.exception( 'could not create SFTP directory for user %s', username) - raise Exception( + raise GVAFileException( "could not create SFTP directory for user %s" % username) return sftp_directory From 7aee6c8838bc8773c272b5d98c82f6337d5ac2aa Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Thu, 25 Dec 2014 23:53:58 +0100 Subject: [PATCH 6/9] use full paths for invoked commands --- gvafile/osusers/tasks.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/gvafile/osusers/tasks.py b/gvafile/osusers/tasks.py index 65c8091..571046f 100644 --- a/gvafile/osusers/tasks.py +++ b/gvafile/osusers/tasks.py @@ -19,6 +19,10 @@ from gvafile.exceptions import GVAFileException _logger = get_task_logger(__name__) +SUDO_CMD = '/usr/bin/sudo' +INSTALL_CMD = '/usr/bin/install' +SETFACL_CMD = '/usr/bin/setfacl' + def _build_sftp_directory_name(username): """ @@ -53,11 +57,11 @@ def setup_file_sftp_userdir(username): sftp_directory = _build_sftp_directory_name(username) try: subprocess.check_output([ - 'sudo', 'install', '-o', username, '-g', username, '-m', '0750', - '-d', sftp_directory], stderr=subprocess.STDOUT) + SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username, + '-m', '0750', '-d', sftp_directory], stderr=subprocess.STDOUT) subprocess.check_output([ - 'sudo' 'setfacl', '-r', '-m', 'www-data:--x', sftp_directory], - stderr=subprocess.STDOUT) + SUDO_CMD, SETFACL_CMD, '-r', '-m', 'www-data:--x', + sftp_directory], stderr=subprocess.STDOUT) except subprocess.CalledProcessError: _logger.exception( 'could not create SFTP directory for user %s', username) From 1fe16eda45e739c5f0ef223fb1464b9e3b5ba2df Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Fri, 26 Dec 2014 00:11:26 +0100 Subject: [PATCH 7/9] remove invalid command option from setfacl call --- gvafile/osusers/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gvafile/osusers/tasks.py b/gvafile/osusers/tasks.py index 571046f..978c3fd 100644 --- a/gvafile/osusers/tasks.py +++ b/gvafile/osusers/tasks.py @@ -60,7 +60,7 @@ def setup_file_sftp_userdir(username): SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username, '-m', '0750', '-d', sftp_directory], stderr=subprocess.STDOUT) subprocess.check_output([ - SUDO_CMD, SETFACL_CMD, '-r', '-m', 'www-data:--x', + SUDO_CMD, SETFACL_CMD, '-m', 'www-data:--x', sftp_directory], stderr=subprocess.STDOUT) except subprocess.CalledProcessError: _logger.exception( From 3218d58ee4caf46e1354be5d9c2c259bb67dd586 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Fri, 26 Dec 2014 00:39:53 +0100 Subject: [PATCH 8/9] implement delete SFTP and setup/delete for mail base directories --- docs/changelog.rst | 2 ++ gvafile/osusers/tasks.py | 70 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 53fa8dd..07449fa 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,5 +1,7 @@ Changelog ========= +* :feature:`-` implement tasks for creating and deleting SFTP and mail base directories + * :release:`0.0.1 <2014-12-25>` * :feature:`-` initial setup of django application and celery worker diff --git a/gvafile/osusers/tasks.py b/gvafile/osusers/tasks.py index 978c3fd..e386a99 100644 --- a/gvafile/osusers/tasks.py +++ b/gvafile/osusers/tasks.py @@ -22,6 +22,7 @@ _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 _build_sftp_directory_name(username): @@ -46,8 +47,6 @@ def setup_file_sftp_userdir(username): This task creates the home directory for an SFTP user if it does not exist yet. - The task is rejected if the directory creation fails. - :param str username: the user name :raises Exception: if the SFTP directory of the user cannot be created :return: the created directory name @@ -72,14 +71,75 @@ def setup_file_sftp_userdir(username): @shared_task def delete_file_sftp_userdir(username): - pass + """ + 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: + _logger.exception( + 'could not remove SFTP directory for user %s', username) + raise GVAFileException( + "could not remove SFTP directory for user %s" % username) + return sftp_directory @shared_task def setup_file_mail_userdir(username): - pass + """ + 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', '0750', '-d', mail_directory], stderr=subprocess.STDOUT) + except subprocess.CalledProcessError: + _logger.exception( + 'could not create mail base directory for user %s', username) + raise GVAFileException( + "could not create mail base directory for user %s" % username) + return mail_directory @shared_task def delete_file_mail_userdir(username): - pass + """ + 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: + _logger.exception( + 'could not remove mail base directory of user %s', username) + raise GVAFileException( + "could not remove mail base directory of user %s" % username) + return mail_directory From 04af89931ab3ddd902a320b531631e5e071f7e09 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Fri, 26 Dec 2014 00:48:35 +0100 Subject: [PATCH 9/9] set version number and release date --- docs/changelog.rst | 1 + docs/conf.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 07449fa..15f7f1f 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,7 @@ Changelog ========= +* :release:`0.0.2 <2014-12-26>` * :feature:`-` implement tasks for creating and deleting SFTP and mail base directories * :release:`0.0.1 <2014-12-25>` diff --git a/docs/conf.py b/docs/conf.py index daa0c8d..7738860 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -62,9 +62,9 @@ copyright = u'2014, Jan Dittberner' # built documents. # # The short X.Y version. -version = '0.0.1' +version = '0.0.2' # The full version, including alpha/beta/rc tags. -release = '0.0.1' +release = '0.0.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages.