diff --git a/docs/changelog.rst b/docs/changelog.rst index 7f82ab1..70036d8 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,9 @@ Changelog ========= +* :release:`0.2.2 <2014-12-26>` +* :feature:`-` home and mail base directory creation + * :release:`0.2.1 <2014-12-17>` * :support:`-` update Django to 1.7.1, update other dependencies, drop South * :bug:`-` wrap :py:meth:`ousers.models.UserManager.create_user` in diff --git a/docs/conf.py b/docs/conf.py index 6b5720c..bb980fe 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -55,9 +55,9 @@ copyright = u'2014, Jan Dittberner' # built documents. # # The short X.Y version. -version = '0.2.1' +version = '0.2.2' # The full version, including alpha/beta/rc tags. -release = '0.2.1' +release = '0.2.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/gnuviechadmin/gnuviechadmin/settings/base.py b/gnuviechadmin/gnuviechadmin/settings/base.py index c871d84..5442654 100644 --- a/gnuviechadmin/gnuviechadmin/settings/base.py +++ b/gnuviechadmin/gnuviechadmin/settings/base.py @@ -280,10 +280,11 @@ CELERY_RESULT_PERSISTENT = True CELERY_TASK_RESULT_EXPIRES = None CELERY_ROUTES = ( 'osusers.tasks.LdapRouter', + 'osusers.tasks.FileRouter', ) -CELERY_ACCEPT_CONTENT = ['yaml'] -CELERY_TASK_SERIALIZER = 'yaml' -CELERY_RESULT_SERIALIZER = 'yaml' +CELERY_ACCEPT_CONTENT = ['pickle', 'yaml', 'json'] +CELERY_TASK_SERIALIZER = 'json' +CELERY_RESULT_SERIALIZER = 'json' ########## END CELERY CONFIGURATION diff --git a/gnuviechadmin/osusers/admin.py b/gnuviechadmin/osusers/admin.py index 4e74a04..aa85d96 100644 --- a/gnuviechadmin/osusers/admin.py +++ b/gnuviechadmin/osusers/admin.py @@ -4,12 +4,9 @@ from django.contrib import admin from .models import ( AdditionalGroup, - DeleteTaskResult, Group, - GroupTaskResult, Shadow, User, - UserTaskResult, ) PASSWORD_MISMATCH_ERROR = _("Passwords don't match") @@ -25,30 +22,6 @@ class ShadowInline(admin.TabularInline): can_delete = False -class TaskResultInline(admin.TabularInline): - can_delete = False - extra = 0 - readonly_fields = ['task_uuid', 'task_name', 'is_finished', 'is_success', - 'state', 'result_body'] - - def get_queryset(self, request): - qs = super(TaskResultInline, self).get_queryset(request) - for entry in qs: - entry.update_taskstatus() - return qs - - def has_add_permission(self, request, obj=None): - return False - - -class UserTaskResultInline(TaskResultInline): - model = UserTaskResult - - -class GroupTaskResultInline(TaskResultInline): - model = GroupTaskResult - - class UserCreationForm(forms.ModelForm): """ A form for creating system users. @@ -91,9 +64,10 @@ class UserCreationForm(forms.ModelForm): class UserAdmin(admin.ModelAdmin): - inlines = [AdditionalGroupInline, ShadowInline, UserTaskResultInline] + actions = ['perform_delete_selected'] readonly_fields = ['uid'] add_form = UserCreationForm + inlines = [AdditionalGroupInline, ShadowInline] add_fieldsets = ( (None, { @@ -115,37 +89,42 @@ class UserAdmin(admin.ModelAdmin): defaults.update(kwargs) return super(UserAdmin, self).get_form(request, obj, **defaults) - def get_inline_instances(self, request, obj=None): - if obj is None: - return [] - return super(UserAdmin, self).get_inline_instances(request, obj) + def get_readonly_fields(self, request, obj=None): + if obj: + return ['uid'] + return [] + + def perform_delete_selected(self, request, queryset): + for user in queryset.all(): + user.delete() + perform_delete_selected.short_description = _('Delete selected users') + + def get_actions(self, request): + actions = super(UserAdmin, self).get_actions(request) + if 'delete_selected' in actions: + del actions['delete_selected'] + return actions class GroupAdmin(admin.ModelAdmin): - inlines = [GroupTaskResultInline] + actions = ['perform_delete_selected'] def get_inline_instances(self, request, obj=None): if obj is None: return [] return super(GroupAdmin, self).get_inline_instances(request, obj) + def perform_delete_selected(self, request, queryset): + for group in queryset.all(): + group.delete() + perform_delete_selected.short_description = _('Delete selected groups') -class DeleteTaskResultAdmin(admin.ModelAdmin): - readonly_fields = ['task_uuid', 'task_name', 'modeltype', 'modelname', - 'is_finished', 'is_success', 'state', 'result_body'] - list_display = ('task_uuid', 'task_name', 'modeltype', 'modelname', - 'is_finished', 'state') - - def has_add_permission(self, request, obj=None): - return False - - def get_queryset(self, request): - qs = super(DeleteTaskResultAdmin, self).get_queryset(request) - for entry in qs: - entry.update_taskstatus() - return qs + def get_actions(self, request): + actions = super(GroupAdmin, self).get_actions(request) + if 'delete_selected' in actions: + del actions['delete_selected'] + return actions admin.site.register(Group, GroupAdmin) admin.site.register(User, UserAdmin) -admin.site.register(DeleteTaskResult, DeleteTaskResultAdmin) diff --git a/gnuviechadmin/osusers/migrations/0002_auto_20141226_1456.py b/gnuviechadmin/osusers/migrations/0002_auto_20141226_1456.py new file mode 100644 index 0000000..0c90043 --- /dev/null +++ b/gnuviechadmin/osusers/migrations/0002_auto_20141226_1456.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('osusers', '0001_initial'), + ] + + operations = [ + migrations.DeleteModel( + name='DeleteTaskResult', + ), + migrations.RemoveField( + model_name='grouptaskresult', + name='group', + ), + migrations.DeleteModel( + name='GroupTaskResult', + ), + migrations.RemoveField( + model_name='usertaskresult', + name='user', + ), + migrations.DeleteModel( + name='UserTaskResult', + ), + ] diff --git a/gnuviechadmin/osusers/models.py b/gnuviechadmin/osusers/models.py index 801f6ba..f3a524b 100644 --- a/gnuviechadmin/osusers/models.py +++ b/gnuviechadmin/osusers/models.py @@ -1,4 +1,7 @@ +from __future__ import unicode_literals + from datetime import date +import logging import os from django.db import models, transaction @@ -19,43 +22,23 @@ from .tasks import ( add_ldap_user_to_group, create_ldap_group, create_ldap_user, + delete_file_mail_userdir, + delete_file_sftp_userdir, delete_ldap_group_if_empty, delete_ldap_user, remove_ldap_user_from_group, + setup_file_mail_userdir, + setup_file_sftp_userdir, ) +logger = logging.getLogger(__name__) + + CANNOT_USE_PRIMARY_GROUP_AS_ADDITIONAL = _( "You can not use a user's primary group.") -class TaskResult(TimeStampedModel, models.Model): - - task_uuid = models.CharField(primary_key=True, max_length=64, blank=False) - task_name = models.CharField(max_length=255, blank=False, db_index=True) - is_finished = models.BooleanField(default=False) - is_success = models.BooleanField(default=False) - state = models.CharField(max_length=10) - result_body = models.TextField(blank=True) - - class Meta: - abstract = True - - def _set_result_fields(self, asyncresult): - if asyncresult.ready(): - self.is_finished = True - self.is_success = asyncresult.state == 'SUCCESS' - self.result_body = str(asyncresult.result) - self.state = asyncresult.state - asyncresult.get(no_ack=False) - - def update_taskstatus(self): - if not self.is_finished: - asyncresult = AsyncResult(self.task_uuid) - self._set_result_fields(asyncresult) - self.save() - - class GroupManager(models.Manager): def get_next_gid(self): @@ -84,74 +67,20 @@ class Group(TimeStampedModel, models.Model): def __str__(self): return '{0} ({1})'.format(self.groupname, self.gid) + @transaction.atomic def save(self, *args, **kwargs): super(Group, self).save(*args, **kwargs) - GroupTaskResult.objects.create_grouptaskresult( - self, - create_ldap_group.delay(self.groupname, self.gid, self.descr), - 'create_ldap_group' - ) + dn = create_ldap_group.delay( + self.groupname, self.gid, self.descr).get() + logger.info("created LDAP group with dn %s", dn) return self + @transaction.atomic def delete(self, *args, **kwargs): - DeleteTaskResult.objects.create_deletetaskresult( - 'group', self.groupname, - delete_ldap_group_if_empty.delay(self.groupname), - 'delete_ldap_group_if_empty' - ) + delete_ldap_group_if_empty.delay(self.groupname).get() super(Group, self).delete(*args, **kwargs) -class TaskResultManager(models.Manager): - - def create(self, asyncresult, task_name): - result = self.model( - task_uuid=asyncresult.task_id, task_name=task_name - ) - result._set_result_fields(asyncresult) - return result - - -class DeleteTaskResultManager(TaskResultManager): - - def create_deletetaskresult( - self, modeltype, modelname, asyncresult, task_name - ): - taskresult = super(DeleteTaskResultManager, self).create( - asyncresult, task_name) - taskresult.modeltype = modeltype - taskresult.modelname = modelname - taskresult.save() - return taskresult - - -class DeleteTaskResult(TaskResult): - - modeltype = models.CharField(max_length=20, db_index=True) - modelname = models.CharField(max_length=255) - - objects = DeleteTaskResultManager() - - -class GroupTaskResultManager(TaskResultManager): - - def create_grouptaskresult( - self, group, asyncresult, task_name, commit=False - ): - taskresult = super(GroupTaskResultManager, self).create( - asyncresult, task_name) - taskresult.group = group - taskresult.save() - return taskresult - - -class GroupTaskResult(TaskResult): - - group = models.ForeignKey(Group) - - objects = GroupTaskResultManager() - - class UserManager(models.Manager): def get_next_uid(self): @@ -215,6 +144,7 @@ class User(TimeStampedModel, models.Model): def __str__(self): return '{0} ({1})'.format(self.username, self.uid) + @transaction.atomic def set_password(self, password): if hasattr(self, 'shadow'): self.shadow.set_password(password) @@ -222,67 +152,42 @@ class User(TimeStampedModel, models.Model): self.shadow = Shadow.objects.create_shadow( user=self, password=password ) - UserTaskResult.objects.create_usertaskresult( - self, - create_ldap_user.delay( - self.username, self.uid, self.group.gid, self.gecos, - self.homedir, self.shell, password - ), - 'create_ldap_user', - commit=True - ) + dn = create_ldap_user.delay( + self.username, self.uid, self.group.gid, self.gecos, + self.homedir, self.shell, password + ).get() + logging.info("set LDAP password for %s", dn) + @transaction.atomic def save(self, *args, **kwargs): - UserTaskResult.objects.create_usertaskresult( - self, - create_ldap_user.delay( - self.username, self.uid, self.group.gid, self.gecos, - self.homedir, self.shell, password=None - ), - 'create_ldap_user' - ) + dn = create_ldap_user.delay( + self.username, self.uid, self.group.gid, self.gecos, + self.homedir, self.shell, password=None).get() + sftp_dir = setup_file_sftp_userdir.delay(self.username).get() + mail_dir = setup_file_mail_userdir.delay(self.username).get() + logger.info( + "created user %(user)s with LDAP dn %(dn)s, home directory " + "%(homedir)s and mail base directory %(maildir)s.", { + 'user': self, 'dn': dn, + 'homedir': sftp_dir, 'maildir': mail_dir + }) return super(User, self).save(*args, **kwargs) + @transaction.atomic def delete(self, *args, **kwargs): - for group in [ - ag.group for ag in AdditionalGroup.objects.filter(user=self) - ]: - DeleteTaskResult.objects.create_deletetaskresult( - 'usergroup', - '{0} in {1}'.format(self.username, group.groupname), - remove_ldap_user_from_group.delay( - self.username, group.groupname), - 'remove_ldap_user_from_group', - ) - DeleteTaskResult.objects.create_deletetaskresult( - 'user', self.username, - delete_ldap_user.delay(self.username), - 'delete_ldap_user' - ) + delete_file_mail_userdir.delay(self.username).get() + delete_file_sftp_userdir.delay(self.username).get() + for group in [ag.group for ag in self.additionalgroup_set.all()]: + remove_ldap_user_from_group.delay( + self.username, group.groupname).get() + delete_ldap_user.delay(self.username).get() self.group.delete() super(User, self).delete(*args, **kwargs) -class UserTaskResultManager(TaskResultManager): - - def create_usertaskresult( - self, user, asyncresult, task_name, commit=False - ): - taskresult = self.create(asyncresult, task_name) - taskresult.user = user - taskresult.save() - return taskresult - - -class UserTaskResult(TaskResult): - - user = models.ForeignKey(User) - - objects = UserTaskResultManager() - - class ShadowManager(models.Manager): + @transaction.atomic def create_shadow(self, user, password): changedays = (timezone.now().date() - date(1970, 1, 1)).days shadow = self.create( @@ -356,23 +261,16 @@ class AdditionalGroup(TimeStampedModel, models.Model): if self.user.group == self.group: raise ValidationError(CANNOT_USE_PRIMARY_GROUP_AS_ADDITIONAL) + @transaction.atomic def save(self, *args, **kwargs): - GroupTaskResult.objects.create_grouptaskresult( - self.group, - add_ldap_user_to_group.delay( - self.user.username, self.group.groupname), - 'add_ldap_user_to_group' - ) + add_ldap_user_to_group.delay( + self.user.username, self.group.groupname).get() super(AdditionalGroup, self).save(*args, **kwargs) + @transaction.atomic def delete(self, *args, **kwargs): - DeleteTaskResult.objects.create_deletetaskresult( - 'usergroup', - str(self), - remove_ldap_user_from_group.delay( - self.user.username, self.group.groupname), - 'remove_ldap_user_from_group' - ) + remove_ldap_user_from_group.delay( + self.user.username, self.group.groupname).get() super(AdditionalGroup, self).delete(*args, **kwargs) def __str__(self): diff --git a/gnuviechadmin/osusers/tasks.py b/gnuviechadmin/osusers/tasks.py index 049eb65..7edd7c7 100644 --- a/gnuviechadmin/osusers/tasks.py +++ b/gnuviechadmin/osusers/tasks.py @@ -13,6 +13,16 @@ class LdapRouter(object): return None +class FileRouter(object): + + def route_for_task(self, task, args=None, kwargs=None): + if 'file' in task: + return {'exchange': 'file', + 'exchange_type': 'direct', + 'queue': 'file'} + return None + + @shared_task def create_ldap_group(groupname, gid, descr): pass @@ -41,3 +51,23 @@ def delete_ldap_user(username): @shared_task def delete_ldap_group_if_empty(groupname): pass + + +@shared_task +def setup_file_sftp_userdir(username): + pass + + +@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