Merge branch 'release/0.2.2' into production
* release/0.2.2: set version number document homedir creation feature create directories for new users add stub tasks for file system operations make user and group management more robust
This commit is contained in:
commit
3e3db481d2
7 changed files with 145 additions and 203 deletions
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
31
gnuviechadmin/osusers/migrations/0002_auto_20141226_1456.py
Normal file
31
gnuviechadmin/osusers/migrations/0002_auto_20141226_1456.py
Normal file
|
@ -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',
|
||||
),
|
||||
]
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue