From 81f1faee6c0e5f207d141780e9b758f576d4b8e5 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 25 May 2014 23:33:37 +0200 Subject: [PATCH 01/48] add celery dependency --- requirements/base.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements/base.txt b/requirements/base.txt index 2cedcab..a4cc9be 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -6,3 +6,4 @@ logutils==0.3.3 South==0.8.4 psycopg2==2.5.3 passlib==1.6.2 +celery==3.1.11 From 97634bb36afeec6046ab6109a9669b685357986e Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 25 May 2014 23:34:27 +0200 Subject: [PATCH 02/48] add celery app to project --- gnuviechadmin/gnuviechadmin/__init__.py | 1 + gnuviechadmin/gnuviechadmin/celery.py | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 gnuviechadmin/gnuviechadmin/celery.py diff --git a/gnuviechadmin/gnuviechadmin/__init__.py b/gnuviechadmin/gnuviechadmin/__init__.py index e69de29..bd9391f 100644 --- a/gnuviechadmin/gnuviechadmin/__init__.py +++ b/gnuviechadmin/gnuviechadmin/__init__.py @@ -0,0 +1 @@ +from gnuviechadmin.celery import app as celery_app diff --git a/gnuviechadmin/gnuviechadmin/celery.py b/gnuviechadmin/gnuviechadmin/celery.py new file mode 100644 index 0000000..5a48fbf --- /dev/null +++ b/gnuviechadmin/gnuviechadmin/celery.py @@ -0,0 +1,21 @@ +from __future__ import absolute_import + +import os + +from celery import Celery + +from django.conf import settings + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', + 'gnuviechadmin.settings.production') + + +app = Celery('gnuviechadmin') + +app.config_from_object('django.conf:settings') +app.autodiscover_tasks(lambda: settings.INSTALLED_APPS) + + +@app.task(bind=True) +def debug_task(self): + print('Request: {0!r}'.format(self.request)) From 86b8f03704cb7b8e596c9da0c3a73ffae20e0ffa Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 25 May 2014 23:35:06 +0200 Subject: [PATCH 03/48] add celery configuration to settings --- gnuviechadmin/gnuviechadmin/settings/base.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/gnuviechadmin/gnuviechadmin/settings/base.py b/gnuviechadmin/gnuviechadmin/settings/base.py index 0a1b858..768ce96 100644 --- a/gnuviechadmin/gnuviechadmin/settings/base.py +++ b/gnuviechadmin/gnuviechadmin/settings/base.py @@ -279,6 +279,14 @@ SOUTH_TESTS_MIGRATE = False ########## END SOUTH CONFIGURATION +########## CELERY CONFIGURATION +BROKER_URL = get_env_variable('GVA_BROKER_URL') +CELERY_RESULT_BACKEND = 'amqp' +CELERY_RESULT_PERSISTENT = True +CELERY_TASK_RESULT_EXPIRES = None +########## END CELERY CONFIGURATION + + ########## CUSTOM APP CONFIGURATION OSUSER_MINUID = int(get_env_variable('GVA_MIN_OS_UID')) OSUSER_MINGID = int(get_env_variable('GVA_MIN_OS_GID')) From caab322bebf68a61e8cf2dbaa1db84d777fafe04 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 25 May 2014 23:35:14 +0200 Subject: [PATCH 04/48] implement user creation in osusers.admin - add osusers.admin.UserCreationForm - add dummy osusers.tasks implementation with create_ldap_group and create_ldap_user - fix UserManager.get_next_username - add proper transaction handling in UserManager.create_user - add calls to create_ldap_user and create_ldap_group to UserManager.create_user --- gnuviechadmin/osusers/admin.py | 68 +++++++++++++++++++++++++++++++++ gnuviechadmin/osusers/models.py | 24 ++++++------ gnuviechadmin/osusers/tasks.py | 13 +++++++ 3 files changed, 94 insertions(+), 11 deletions(-) create mode 100644 gnuviechadmin/osusers/tasks.py diff --git a/gnuviechadmin/osusers/admin.py b/gnuviechadmin/osusers/admin.py index e0e9adc..62928fc 100644 --- a/gnuviechadmin/osusers/admin.py +++ b/gnuviechadmin/osusers/admin.py @@ -1,3 +1,5 @@ +from django import forms +from django.utils.translation import ugettext as _ from django.contrib import admin from .models import ( @@ -7,6 +9,8 @@ from .models import ( User, ) +PASSWORD_MISMATCH_ERROR = _("Passwords don't match") + class AdditionalGroupInline(admin.TabularInline): model = AdditionalGroup @@ -18,9 +22,73 @@ class ShadowInline(admin.TabularInline): can_delete = False +class UserCreationForm(forms.ModelForm): + """ + A form for creating system users. + + """ + password1 = forms.CharField(label=_('Password'), + widget=forms.PasswordInput) + password2 = forms.CharField(label=_('Password (again)'), + widget=forms.PasswordInput) + + class Meta: + model = User + fields = [] + + def clean_password2(self): + """ + Check that the two password entries match. + + """ + password1 = self.cleaned_data.get('password1') + password2 = self.cleaned_data.get('password2') + if password1 and password2 and password1 != password2: + raise forms.ValidationError(PASSWORD_MISMATCH_ERROR) + return password2 + + def save(self, commit=True): + """ + Save the provided password in hashed format. + + """ + user = User.objects.create_user( + password=self.cleaned_data['password1'], commit=commit) + return user + + def save_m2m(self): + pass + + class UserAdmin(admin.ModelAdmin): inlines = [AdditionalGroupInline, ShadowInline] readonly_fields = ['uid'] + add_form = UserCreationForm + + add_fieldsets = ( + (None, { + 'classes': ('wide',), + 'fields': ('password1', 'password2')}), + ) + + def get_form(self, request, obj=None, **kwargs): + """ + Use special form during user creation. + + """ + defaults = {} + if obj is None: + defaults.update({ + 'form': self.add_form, + 'fields': admin.util.flatten_fieldsets(self.add_fieldsets), + }) + 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) admin.site.register(Group) diff --git a/gnuviechadmin/osusers/models.py b/gnuviechadmin/osusers/models.py index ecb9beb..1ecf82c 100644 --- a/gnuviechadmin/osusers/models.py +++ b/gnuviechadmin/osusers/models.py @@ -1,7 +1,7 @@ from datetime import date import os -from django.db import models, transaction +from django.db import models from django.conf import settings from django.core.exceptions import ValidationError from django.utils import timezone @@ -13,6 +13,11 @@ from model_utils.models import TimeStampedModel from passlib.hash import sha512_crypt from passlib.utils import generate_password +from .tasks import ( + create_ldap_group, + create_ldap_user, +) + class GroupManager(models.Manager): @@ -59,7 +64,7 @@ class UserManager(models.Manager): for user in self.values('username').filter( username__startswith=settings.OSUSER_USERNAME_PREFIX).order_by( 'username'): - if user == nextuser: + if user['username'] == nextuser: count += 1 nextuser = usernameformat.format( settings.OSUSER_USERNAME_PREFIX, count) @@ -67,7 +72,7 @@ class UserManager(models.Manager): break return nextuser - def create_user(self, username=None, password=None): + def create_user(self, username=None, password=None, commit=False): uid = self.get_next_uid() gid = Group.objects.get_next_gid() if username is None: @@ -75,19 +80,16 @@ class UserManager(models.Manager): if password is None: password = generate_password() homedir = os.path.join(settings.OSUSER_HOME_BASEPATH, username) - autocommit = transaction.get_autocommit() - if autocommit: - transaction.set_autocommit(False) group = Group.objects.create(groupname=username, gid=gid) + create_ldap_group.delay(group) user = self.create(username=username, group=group, uid=uid, homedir=homedir, shell=settings.OSUSER_DEFAULT_SHELL) + create_ldap_user.delay(user, password) shadow = Shadow.objects.create_shadow(user=user, password=password) - user.save() - shadow.save() - transaction.commit() - if autocommit: - transaction.set_autocommit(True) + if commit: + user.save() + shadow.save() return user diff --git a/gnuviechadmin/osusers/tasks.py b/gnuviechadmin/osusers/tasks.py new file mode 100644 index 0000000..7382c99 --- /dev/null +++ b/gnuviechadmin/osusers/tasks.py @@ -0,0 +1,13 @@ +from __future__ import absolute_import + +from celery import shared_task + + +@shared_task +def create_ldap_group(group): + return group + + +@shared_task +def create_ldap_user(user, password): + return user, password From 1e717556ba18b5fe2af1ce2308c120d063c5762a Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Fri, 30 May 2014 12:17:04 +0200 Subject: [PATCH 05/48] refine ldap create commands - define gnuviechadmin.settins.base.CELERY_ROUTES to properly route ldap tasks - return dummy values from osusers.tasks - add billiard, kombu and pytz to requirements/base.txt --- gnuviechadmin/gnuviechadmin/settings/base.py | 4 ++++ gnuviechadmin/osusers/tasks.py | 4 ++-- requirements/base.txt | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/gnuviechadmin/gnuviechadmin/settings/base.py b/gnuviechadmin/gnuviechadmin/settings/base.py index 768ce96..e9a7e8d 100644 --- a/gnuviechadmin/gnuviechadmin/settings/base.py +++ b/gnuviechadmin/gnuviechadmin/settings/base.py @@ -284,6 +284,10 @@ BROKER_URL = get_env_variable('GVA_BROKER_URL') CELERY_RESULT_BACKEND = 'amqp' CELERY_RESULT_PERSISTENT = True CELERY_TASK_RESULT_EXPIRES = None +CELERY_ROUTES = { + 'osusers.tasks.create_ldap_group': {'queue': 'ldap'}, + 'osusers.tasks.create_ldap_user': {'queue': 'ldap'}, +} ########## END CELERY CONFIGURATION diff --git a/gnuviechadmin/osusers/tasks.py b/gnuviechadmin/osusers/tasks.py index 7382c99..33af75f 100644 --- a/gnuviechadmin/osusers/tasks.py +++ b/gnuviechadmin/osusers/tasks.py @@ -5,9 +5,9 @@ from celery import shared_task @shared_task def create_ldap_group(group): - return group + return group.groupname @shared_task def create_ldap_user(user, password): - return user, password + return user.username diff --git a/requirements/base.txt b/requirements/base.txt index a4cc9be..adc811d 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -7,3 +7,6 @@ South==0.8.4 psycopg2==2.5.3 passlib==1.6.2 celery==3.1.11 +billiard==3.3.0.17 +kombu==3.0.16 +pytz==2014.3 From 6eb74f5b79b7026e3524c73ffba4ddec7b80fcac Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Fri, 30 May 2014 17:10:22 +0200 Subject: [PATCH 06/48] add more ldap tasks - add custom celery router osusers.tasks.LdapRouter - add tasks add_ldap_user_to_group, delete_ldap_group_if_empty, delete_ldap_user and remove_ldap_user_from_group - implement osusers.models.Group.save and osusers.models.Group.delete - implement save, delete and set_password methods in osusers.models.User - implement save and delete methods in osusers.models.AdditionalGroup --- gnuviechadmin/gnuviechadmin/settings/base.py | 7 ++- gnuviechadmin/osusers/models.py | 45 ++++++++++++++++++-- gnuviechadmin/osusers/tasks.py | 30 +++++++++++++ 3 files changed, 75 insertions(+), 7 deletions(-) diff --git a/gnuviechadmin/gnuviechadmin/settings/base.py b/gnuviechadmin/gnuviechadmin/settings/base.py index e9a7e8d..6126077 100644 --- a/gnuviechadmin/gnuviechadmin/settings/base.py +++ b/gnuviechadmin/gnuviechadmin/settings/base.py @@ -284,10 +284,9 @@ BROKER_URL = get_env_variable('GVA_BROKER_URL') CELERY_RESULT_BACKEND = 'amqp' CELERY_RESULT_PERSISTENT = True CELERY_TASK_RESULT_EXPIRES = None -CELERY_ROUTES = { - 'osusers.tasks.create_ldap_group': {'queue': 'ldap'}, - 'osusers.tasks.create_ldap_user': {'queue': 'ldap'}, -} +CELERY_ROUTES = ( + 'osusers.tasks.LdapRouter', +) ########## END CELERY CONFIGURATION diff --git a/gnuviechadmin/osusers/models.py b/gnuviechadmin/osusers/models.py index 1ecf82c..c513574 100644 --- a/gnuviechadmin/osusers/models.py +++ b/gnuviechadmin/osusers/models.py @@ -14,8 +14,12 @@ from passlib.hash import sha512_crypt from passlib.utils import generate_password from .tasks import ( + add_ldap_user_to_group, create_ldap_group, create_ldap_user, + delete_ldap_group_if_empty, + delete_ldap_user, + remove_ldap_user_from_group, ) @@ -47,6 +51,15 @@ class Group(TimeStampedModel, models.Model): def __str__(self): return '{0} ({1})'.format(self.groupname, self.gid) + def save(self, *args, **kwargs): + super(Group, self).save(*args, **kwargs) + create_ldap_group.delay(self) + return self + + def delete(self, *args, **kwargs): + delete_ldap_group_if_empty.delay(self) + super(Group, self).delete(*args, **kwargs) + class UserManager(models.Manager): @@ -85,11 +98,10 @@ class UserManager(models.Manager): user = self.create(username=username, group=group, uid=uid, homedir=homedir, shell=settings.OSUSER_DEFAULT_SHELL) - create_ldap_user.delay(user, password) - shadow = Shadow.objects.create_shadow(user=user, password=password) + Shadow.objects.create_shadow(user=user, password=password) + user.set_password(password) if commit: user.save() - shadow.save() return user @@ -113,6 +125,23 @@ class User(TimeStampedModel, models.Model): def __str__(self): return '{0} ({1})'.format(self.username, self.uid) + def set_password(self, password): + create_ldap_user.delay(self, password) + + def save(self, *args, **kwargs): + create_ldap_user.delay(self, password=None) + return super(User, self).save(*args, **kwargs) + + def delete(self, *args, **kwargs): + for group in [ + ag.group for ag in AdditionalGroup.objects.filter(user=self) + ]: + remove_ldap_user_from_group.delay(self.username, group.groupname) + delete_ldap_user.delay(self) + delete_ldap_group_if_empty.delay(self.group) + self.group.delete() + super(User, self).delete(*args, **kwargs) + class ShadowManager(models.Manager): @@ -187,5 +216,15 @@ class AdditionalGroup(TimeStampedModel, models.Model): raise ValidationError(_( "You can not use a user's primary group.")) + def save(self, *args, **kwargs): + add_ldap_user_to_group.delay( + self.user.username, self.group.groupname) + super(AdditionalGroup, self).save(*args, **kwargs) + + def delete(self, *args, **kwargs): + remove_ldap_user_from_group.delay( + self.user.username, self.group.groupname) + super(AdditionalGroup, self).delete(*args, **kwargs) + def __str__(self): return '{0} in {1}'.format(self.user, self.group) diff --git a/gnuviechadmin/osusers/tasks.py b/gnuviechadmin/osusers/tasks.py index 33af75f..5eff249 100644 --- a/gnuviechadmin/osusers/tasks.py +++ b/gnuviechadmin/osusers/tasks.py @@ -3,6 +3,16 @@ from __future__ import absolute_import from celery import shared_task +class LdapRouter(object): + + def route_for_task(self, task, args=None, kwargs=None): + if 'ldap' in task: + return {'exchange': 'ldap', + 'exchange_type': 'direct', + 'queue': 'ldap'} + return None + + @shared_task def create_ldap_group(group): return group.groupname @@ -11,3 +21,23 @@ def create_ldap_group(group): @shared_task def create_ldap_user(user, password): return user.username + + +@shared_task +def add_ldap_user_to_group(username, groupname): + pass + + +@shared_task +def remove_ldap_user_from_group(username, groupname): + pass + + +@shared_task +def delete_ldap_user(user): + pass + + +@shared_task +def delete_ldap_group_if_empty(group): + pass From 6a40a5eded8861fd6d83da5486d817a782ec675f Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Fri, 30 May 2014 17:18:42 +0200 Subject: [PATCH 07/48] pass groupname only to delete_ldap_group_if_empty --- gnuviechadmin/osusers/models.py | 4 ++-- gnuviechadmin/osusers/tasks.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gnuviechadmin/osusers/models.py b/gnuviechadmin/osusers/models.py index c513574..ef1287a 100644 --- a/gnuviechadmin/osusers/models.py +++ b/gnuviechadmin/osusers/models.py @@ -57,7 +57,7 @@ class Group(TimeStampedModel, models.Model): return self def delete(self, *args, **kwargs): - delete_ldap_group_if_empty.delay(self) + delete_ldap_group_if_empty.delay(self.groupname) super(Group, self).delete(*args, **kwargs) @@ -138,7 +138,7 @@ class User(TimeStampedModel, models.Model): ]: remove_ldap_user_from_group.delay(self.username, group.groupname) delete_ldap_user.delay(self) - delete_ldap_group_if_empty.delay(self.group) + delete_ldap_group_if_empty.delay(self.group.groupname) self.group.delete() super(User, self).delete(*args, **kwargs) diff --git a/gnuviechadmin/osusers/tasks.py b/gnuviechadmin/osusers/tasks.py index 5eff249..2cf9349 100644 --- a/gnuviechadmin/osusers/tasks.py +++ b/gnuviechadmin/osusers/tasks.py @@ -39,5 +39,5 @@ def delete_ldap_user(user): @shared_task -def delete_ldap_group_if_empty(group): +def delete_ldap_group_if_empty(groupname): pass From 0c5706d886ffffdf601fd7e6ab808e0054f1e4e7 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Fri, 30 May 2014 18:36:26 +0200 Subject: [PATCH 08/48] use YAML for celery serialization - add CELERY_ACCEPT_CONTENT, CELERY_TASK_SERIALIZER and CELERY_RESULT_SERIALIZER in gnuviechadmin.settings.base - add pyaml to requirements/base.txt --- gnuviechadmin/gnuviechadmin/settings/base.py | 3 +++ requirements/base.txt | 1 + 2 files changed, 4 insertions(+) diff --git a/gnuviechadmin/gnuviechadmin/settings/base.py b/gnuviechadmin/gnuviechadmin/settings/base.py index 6126077..2b1322f 100644 --- a/gnuviechadmin/gnuviechadmin/settings/base.py +++ b/gnuviechadmin/gnuviechadmin/settings/base.py @@ -287,6 +287,9 @@ CELERY_TASK_RESULT_EXPIRES = None CELERY_ROUTES = ( 'osusers.tasks.LdapRouter', ) +CELERY_ACCEPT_CONTENT = ['yaml'] +CELERY_TASK_SERIALIZER = 'yaml' +CELERY_RESULT_SERIALIZER = 'yaml' ########## END CELERY CONFIGURATION diff --git a/requirements/base.txt b/requirements/base.txt index adc811d..f3b8dd8 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -10,3 +10,4 @@ celery==3.1.11 billiard==3.3.0.17 kombu==3.0.16 pytz==2014.3 +pyaml==14.05.7 From 865f54ab670bd1564cc0b89d9e8ff39af90b7573 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Fri, 30 May 2014 18:39:51 +0200 Subject: [PATCH 09/48] use primitive fields instead of models for tasks - modify osusers.tasks and osusers.models to avoid serialization of full models for celery tasks and use the required fields only --- gnuviechadmin/osusers/models.py | 16 +++++++++++----- gnuviechadmin/osusers/tasks.py | 10 +++++----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/gnuviechadmin/osusers/models.py b/gnuviechadmin/osusers/models.py index ef1287a..c6206b7 100644 --- a/gnuviechadmin/osusers/models.py +++ b/gnuviechadmin/osusers/models.py @@ -53,7 +53,7 @@ class Group(TimeStampedModel, models.Model): def save(self, *args, **kwargs): super(Group, self).save(*args, **kwargs) - create_ldap_group.delay(self) + create_ldap_group.delay(self.groupname, self.gid, self.descr) return self def delete(self, *args, **kwargs): @@ -94,7 +94,7 @@ class UserManager(models.Manager): password = generate_password() homedir = os.path.join(settings.OSUSER_HOME_BASEPATH, username) group = Group.objects.create(groupname=username, gid=gid) - create_ldap_group.delay(group) + create_ldap_group.delay(group.groupname, group.gid, group.descr) user = self.create(username=username, group=group, uid=uid, homedir=homedir, shell=settings.OSUSER_DEFAULT_SHELL) @@ -126,10 +126,16 @@ class User(TimeStampedModel, models.Model): return '{0} ({1})'.format(self.username, self.uid) def set_password(self, password): - create_ldap_user.delay(self, password) + create_ldap_user.delay( + self.username, self.uid, self.group.id, self.gecos, self.homedir, + self.shell, password + ) def save(self, *args, **kwargs): - create_ldap_user.delay(self, password=None) + create_ldap_user.delay( + self.username, self.uid, self.group.id, self.gecos, self.homedir, + self.shell, password=None + ) return super(User, self).save(*args, **kwargs) def delete(self, *args, **kwargs): @@ -137,7 +143,7 @@ class User(TimeStampedModel, models.Model): ag.group for ag in AdditionalGroup.objects.filter(user=self) ]: remove_ldap_user_from_group.delay(self.username, group.groupname) - delete_ldap_user.delay(self) + delete_ldap_user.delay(self.username) delete_ldap_group_if_empty.delay(self.group.groupname) self.group.delete() super(User, self).delete(*args, **kwargs) diff --git a/gnuviechadmin/osusers/tasks.py b/gnuviechadmin/osusers/tasks.py index 2cf9349..049eb65 100644 --- a/gnuviechadmin/osusers/tasks.py +++ b/gnuviechadmin/osusers/tasks.py @@ -14,13 +14,13 @@ class LdapRouter(object): @shared_task -def create_ldap_group(group): - return group.groupname +def create_ldap_group(groupname, gid, descr): + pass @shared_task -def create_ldap_user(user, password): - return user.username +def create_ldap_user(username, uid, gid, gecos, homedir, shell, password): + pass @shared_task @@ -34,7 +34,7 @@ def remove_ldap_user_from_group(username, groupname): @shared_task -def delete_ldap_user(user): +def delete_ldap_user(username): pass From 59783e986d97d08d3792aaad4a39284318a11015 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Fri, 30 May 2014 21:46:10 +0200 Subject: [PATCH 10/48] implement classes for tracking task status --- gnuviechadmin/osusers/admin.py | 57 ++++++- ...dd_usertaskresult__add_deletetaskresult.py | 147 +++++++++++++++++ ..._task_name__add_index_usertaskresult_ta.py | 113 +++++++++++++ gnuviechadmin/osusers/models.py | 155 ++++++++++++++++-- 4 files changed, 455 insertions(+), 17 deletions(-) create mode 100644 gnuviechadmin/osusers/migrations/0003_auto__add_grouptaskresult__add_usertaskresult__add_deletetaskresult.py create mode 100644 gnuviechadmin/osusers/migrations/0004_auto__add_index_grouptaskresult_task_name__add_index_usertaskresult_ta.py diff --git a/gnuviechadmin/osusers/admin.py b/gnuviechadmin/osusers/admin.py index 62928fc..3a23b82 100644 --- a/gnuviechadmin/osusers/admin.py +++ b/gnuviechadmin/osusers/admin.py @@ -4,9 +4,12 @@ from django.contrib import admin from .models import ( AdditionalGroup, + DeleteTaskResult, Group, + GroupTaskResult, Shadow, User, + UserTaskResult, ) PASSWORD_MISMATCH_ERROR = _("Passwords don't match") @@ -22,6 +25,30 @@ 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. @@ -61,7 +88,7 @@ class UserCreationForm(forms.ModelForm): class UserAdmin(admin.ModelAdmin): - inlines = [AdditionalGroupInline, ShadowInline] + inlines = [AdditionalGroupInline, ShadowInline, UserTaskResultInline] readonly_fields = ['uid'] add_form = UserCreationForm @@ -91,5 +118,31 @@ class UserAdmin(admin.ModelAdmin): return super(UserAdmin, self).get_inline_instances(request, obj) -admin.site.register(Group) +class GroupAdmin(admin.ModelAdmin): + inlines = [GroupTaskResultInline] + + def get_inline_instances(self, request, obj=None): + if obj is None: + return [] + return super(GroupAdmin, self).get_inline_instances(request, obj) + + +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 + + +admin.site.register(Group, GroupAdmin) admin.site.register(User, UserAdmin) +admin.site.register(DeleteTaskResult, DeleteTaskResultAdmin) diff --git a/gnuviechadmin/osusers/migrations/0003_auto__add_grouptaskresult__add_usertaskresult__add_deletetaskresult.py b/gnuviechadmin/osusers/migrations/0003_auto__add_grouptaskresult__add_usertaskresult__add_deletetaskresult.py new file mode 100644 index 0000000..c23b290 --- /dev/null +++ b/gnuviechadmin/osusers/migrations/0003_auto__add_grouptaskresult__add_usertaskresult__add_deletetaskresult.py @@ -0,0 +1,147 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'GroupTaskResult' + db.create_table(u'osusers_grouptaskresult', ( + ('created', self.gf('model_utils.fields.AutoCreatedField')(default=datetime.datetime.now)), + ('modified', self.gf('model_utils.fields.AutoLastModifiedField')(default=datetime.datetime.now)), + ('task_uuid', self.gf('django.db.models.fields.CharField')(max_length=64, primary_key=True)), + ('task_name', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('is_finished', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('is_success', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('state', self.gf('django.db.models.fields.CharField')(max_length=10)), + ('result_body', self.gf('django.db.models.fields.TextField')(blank=True)), + ('group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['osusers.Group'])), + )) + db.send_create_signal(u'osusers', ['GroupTaskResult']) + + # Adding model 'UserTaskResult' + db.create_table(u'osusers_usertaskresult', ( + ('created', self.gf('model_utils.fields.AutoCreatedField')(default=datetime.datetime.now)), + ('modified', self.gf('model_utils.fields.AutoLastModifiedField')(default=datetime.datetime.now)), + ('task_uuid', self.gf('django.db.models.fields.CharField')(max_length=64, primary_key=True)), + ('task_name', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('is_finished', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('is_success', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('state', self.gf('django.db.models.fields.CharField')(max_length=10)), + ('result_body', self.gf('django.db.models.fields.TextField')(blank=True)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['osusers.User'])), + )) + db.send_create_signal(u'osusers', ['UserTaskResult']) + + # Adding model 'DeleteTaskResult' + db.create_table(u'osusers_deletetaskresult', ( + ('created', self.gf('model_utils.fields.AutoCreatedField')(default=datetime.datetime.now)), + ('modified', self.gf('model_utils.fields.AutoLastModifiedField')(default=datetime.datetime.now)), + ('task_uuid', self.gf('django.db.models.fields.CharField')(max_length=64, primary_key=True)), + ('task_name', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('is_finished', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('is_success', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('state', self.gf('django.db.models.fields.CharField')(max_length=10)), + ('result_body', self.gf('django.db.models.fields.TextField')(blank=True)), + ('modeltype', self.gf('django.db.models.fields.CharField')(max_length=20, db_index=True)), + ('modelname', self.gf('django.db.models.fields.CharField')(max_length=255)), + )) + db.send_create_signal(u'osusers', ['DeleteTaskResult']) + + + def backwards(self, orm): + # Deleting model 'GroupTaskResult' + db.delete_table(u'osusers_grouptaskresult') + + # Deleting model 'UserTaskResult' + db.delete_table(u'osusers_usertaskresult') + + # Deleting model 'DeleteTaskResult' + db.delete_table(u'osusers_deletetaskresult') + + + models = { + u'osusers.additionalgroup': { + 'Meta': {'unique_together': "(('user', 'group'),)", 'object_name': 'AdditionalGroup'}, + 'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['osusers.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['osusers.User']"}) + }, + u'osusers.deletetaskresult': { + 'Meta': {'object_name': 'DeleteTaskResult'}, + 'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}), + 'is_finished': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_success': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'modelname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'modeltype': ('django.db.models.fields.CharField', [], {'max_length': '20', 'db_index': 'True'}), + 'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}), + 'result_body': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '10'}), + 'task_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'task_uuid': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}) + }, + u'osusers.group': { + 'Meta': {'object_name': 'Group'}, + 'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}), + 'descr': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'gid': ('django.db.models.fields.PositiveSmallIntegerField', [], {'unique': 'True', 'primary_key': 'True'}), + 'groupname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '16'}), + 'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}), + 'passwd': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}) + }, + u'osusers.grouptaskresult': { + 'Meta': {'object_name': 'GroupTaskResult'}, + 'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['osusers.Group']"}), + 'is_finished': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_success': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}), + 'result_body': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '10'}), + 'task_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'task_uuid': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}) + }, + u'osusers.shadow': { + 'Meta': {'object_name': 'Shadow'}, + 'changedays': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}), + 'expiredays': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}), + 'gracedays': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'inactdays': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'maxage': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'minage': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}), + 'passwd': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['osusers.User']", 'unique': 'True', 'primary_key': 'True'}) + }, + u'osusers.user': { + 'Meta': {'object_name': 'User'}, + 'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}), + 'gecos': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['osusers.Group']"}), + 'homedir': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}), + 'shell': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'uid': ('django.db.models.fields.PositiveSmallIntegerField', [], {'unique': 'True', 'primary_key': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}) + }, + u'osusers.usertaskresult': { + 'Meta': {'object_name': 'UserTaskResult'}, + 'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}), + 'is_finished': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_success': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}), + 'result_body': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '10'}), + 'task_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'task_uuid': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['osusers.User']"}) + } + } + + complete_apps = ['osusers'] \ No newline at end of file diff --git a/gnuviechadmin/osusers/migrations/0004_auto__add_index_grouptaskresult_task_name__add_index_usertaskresult_ta.py b/gnuviechadmin/osusers/migrations/0004_auto__add_index_grouptaskresult_task_name__add_index_usertaskresult_ta.py new file mode 100644 index 0000000..1657e0d --- /dev/null +++ b/gnuviechadmin/osusers/migrations/0004_auto__add_index_grouptaskresult_task_name__add_index_usertaskresult_ta.py @@ -0,0 +1,113 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding index on 'GroupTaskResult', fields ['task_name'] + db.create_index(u'osusers_grouptaskresult', ['task_name']) + + # Adding index on 'UserTaskResult', fields ['task_name'] + db.create_index(u'osusers_usertaskresult', ['task_name']) + + # Adding index on 'DeleteTaskResult', fields ['task_name'] + db.create_index(u'osusers_deletetaskresult', ['task_name']) + + + def backwards(self, orm): + # Removing index on 'DeleteTaskResult', fields ['task_name'] + db.delete_index(u'osusers_deletetaskresult', ['task_name']) + + # Removing index on 'UserTaskResult', fields ['task_name'] + db.delete_index(u'osusers_usertaskresult', ['task_name']) + + # Removing index on 'GroupTaskResult', fields ['task_name'] + db.delete_index(u'osusers_grouptaskresult', ['task_name']) + + + models = { + u'osusers.additionalgroup': { + 'Meta': {'unique_together': "(('user', 'group'),)", 'object_name': 'AdditionalGroup'}, + 'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['osusers.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['osusers.User']"}) + }, + u'osusers.deletetaskresult': { + 'Meta': {'object_name': 'DeleteTaskResult'}, + 'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}), + 'is_finished': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_success': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'modelname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'modeltype': ('django.db.models.fields.CharField', [], {'max_length': '20', 'db_index': 'True'}), + 'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}), + 'result_body': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '10'}), + 'task_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'task_uuid': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}) + }, + u'osusers.group': { + 'Meta': {'object_name': 'Group'}, + 'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}), + 'descr': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'gid': ('django.db.models.fields.PositiveSmallIntegerField', [], {'unique': 'True', 'primary_key': 'True'}), + 'groupname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '16'}), + 'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}), + 'passwd': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}) + }, + u'osusers.grouptaskresult': { + 'Meta': {'object_name': 'GroupTaskResult'}, + 'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['osusers.Group']"}), + 'is_finished': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_success': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}), + 'result_body': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '10'}), + 'task_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'task_uuid': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}) + }, + u'osusers.shadow': { + 'Meta': {'object_name': 'Shadow'}, + 'changedays': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}), + 'expiredays': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}), + 'gracedays': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'inactdays': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'maxage': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'minage': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}), + 'passwd': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['osusers.User']", 'unique': 'True', 'primary_key': 'True'}) + }, + u'osusers.user': { + 'Meta': {'object_name': 'User'}, + 'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}), + 'gecos': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['osusers.Group']"}), + 'homedir': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}), + 'shell': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'uid': ('django.db.models.fields.PositiveSmallIntegerField', [], {'unique': 'True', 'primary_key': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}) + }, + u'osusers.usertaskresult': { + 'Meta': {'object_name': 'UserTaskResult'}, + 'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}), + 'is_finished': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_success': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}), + 'result_body': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '10'}), + 'task_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'task_uuid': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['osusers.User']"}) + } + } + + complete_apps = ['osusers'] \ No newline at end of file diff --git a/gnuviechadmin/osusers/models.py b/gnuviechadmin/osusers/models.py index c6206b7..468ce31 100644 --- a/gnuviechadmin/osusers/models.py +++ b/gnuviechadmin/osusers/models.py @@ -10,6 +10,8 @@ from django.utils.translation import ugettext as _ from model_utils.models import TimeStampedModel +from celery.result import AsyncResult + from passlib.hash import sha512_crypt from passlib.utils import generate_password @@ -23,6 +25,33 @@ from .tasks import ( ) +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, task_name=self.task_name) + self._set_result_fields(asyncresult) + self.save() + + class GroupManager(models.Manager): def get_next_gid(self): @@ -53,14 +82,65 @@ class Group(TimeStampedModel, models.Model): def save(self, *args, **kwargs): super(Group, self).save(*args, **kwargs) - create_ldap_group.delay(self.groupname, self.gid, self.descr) + GroupTaskResult.objects.create_grouptaskresult( + self, create_ldap_group.delay(self.groupname, self.gid, self.descr) + ) return self def delete(self, *args, **kwargs): - delete_ldap_group_if_empty.delay(self.groupname) + DeleteTaskResult.objects.create_deletetaskresult( + 'group', self.groupname, + delete_ldap_group_if_empty.delay(self.groupname) + ) super(Group, self).delete(*args, **kwargs) +class TaskResultManager(models.Manager): + + def create(self, asyncresult): + result = self.model( + task_uuid=asyncresult.task_id, task_name=asyncresult.task_name + ) + result._set_result_fields(asyncresult) + return result + + +class DeleteTaskResultManager(TaskResultManager): + + def create_deletetaskresult(self, modeltype, modelname, asyncresult): + taskresult = super(DeleteTaskResultManager, self).create( + asyncresult) + 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, commit=False): + taskresult = super(GroupTaskResultManager, self).create( + asyncresult) + 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): @@ -94,7 +174,10 @@ class UserManager(models.Manager): password = generate_password() homedir = os.path.join(settings.OSUSER_HOME_BASEPATH, username) group = Group.objects.create(groupname=username, gid=gid) - create_ldap_group.delay(group.groupname, group.gid, group.descr) + GroupTaskResult.objects.create( + group, + create_ldap_group.delay(group.groupname, group.gid, group.descr) + ) user = self.create(username=username, group=group, uid=uid, homedir=homedir, shell=settings.OSUSER_DEFAULT_SHELL) @@ -126,15 +209,22 @@ class User(TimeStampedModel, models.Model): return '{0} ({1})'.format(self.username, self.uid) def set_password(self, password): - create_ldap_user.delay( - self.username, self.uid, self.group.id, self.gecos, self.homedir, - self.shell, password + UserTaskResult.objects.create_usertaskresult( + self, + create_ldap_user.delay( + self.username, self.uid, self.group.id, self.gecos, + self.homedir, self.shell, password + ), + commit=True ) def save(self, *args, **kwargs): - create_ldap_user.delay( - self.username, self.uid, self.group.id, self.gecos, self.homedir, - self.shell, password=None + UserTaskResult.objects.create_usertaskresult( + self, + create_ldap_user.delay( + self.username, self.uid, self.group.id, self.gecos, + self.homedir, self.shell, password=None + ) ) return super(User, self).save(*args, **kwargs) @@ -142,13 +232,40 @@ class User(TimeStampedModel, models.Model): for group in [ ag.group for ag in AdditionalGroup.objects.filter(user=self) ]: - remove_ldap_user_from_group.delay(self.username, group.groupname) - delete_ldap_user.delay(self.username) - delete_ldap_group_if_empty.delay(self.group.groupname) + DeleteTaskResult.objects.create_deletetaskresult( + 'usergroup', + '{0} in {1}'.format(self.username, group.groupname), + remove_ldap_user_from_group.delay( + self.username, group.groupname) + ) + DeleteTaskResult.objects.create_deletetaskresult( + 'user', self.username, + delete_ldap_user.delay(self.username) + ) + DeleteTaskResult.objects.create_deletetaskresult( + 'group', self.group.groupname, + delete_ldap_group_if_empty.delay(self.group.groupname) + ) self.group.delete() super(User, self).delete(*args, **kwargs) +class UserTaskResultManager(TaskResultManager): + + def create_usertaskresult(self, user, asyncresult, commit=False): + taskresult = self.create(asyncresult) + taskresult.user = user + taskresult.save() + return taskresult + + +class UserTaskResult(TaskResult): + + user = models.ForeignKey(User) + + objects = UserTaskResultManager() + + class ShadowManager(models.Manager): def create_shadow(self, user, password): @@ -223,13 +340,21 @@ class AdditionalGroup(TimeStampedModel, models.Model): "You can not use a user's primary group.")) def save(self, *args, **kwargs): - add_ldap_user_to_group.delay( + res = add_ldap_user_to_group.delay( self.user.username, self.group.groupname) + GroupTaskResult.objects.create( + group=self.group, task_uuid=res.id, + task_name=res.task_name + ) super(AdditionalGroup, self).save(*args, **kwargs) def delete(self, *args, **kwargs): - remove_ldap_user_from_group.delay( - self.user.username, self.group.groupname) + DeleteTaskResult.objects.create_deletetaskresult( + 'usergroup', + str(self), + remove_ldap_user_from_group.delay( + self.user.username, self.group.groupname) + ) super(AdditionalGroup, self).delete(*args, **kwargs) def __str__(self): From d1abe10349f145b369847765ba6e42e1c3593593 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 00:17:57 +0200 Subject: [PATCH 11/48] add release plugin and changelog --- .gitignore | 1 + docs/changelog.rst | 5 +++++ docs/conf.py | 32 +++++++++++++++++++------------- docs/index.rst | 2 +- requirements/local.txt | 1 + 5 files changed, 27 insertions(+), 14 deletions(-) create mode 100644 docs/changelog.rst diff --git a/.gitignore b/.gitignore index 5f8ae64..f25647b 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,4 @@ Desktop.ini .ropeproject/ htmlcov/ tags +_build/ diff --git a/docs/changelog.rst b/docs/changelog.rst new file mode 100644 index 0000000..9826f7d --- /dev/null +++ b/docs/changelog.rst @@ -0,0 +1,5 @@ +Changelog +========= + +* :feature:`-` initial model code for os users +* :feature:`-` initial model code for mail address and mailbox management diff --git a/docs/conf.py b/docs/conf.py index 72bbeb9..228b845 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# pymode:lint_ignore=E501 # # gnuviechadmin documentation build configuration file, created by # sphinx-quickstart on Sun May 18, 2014. @@ -12,7 +13,8 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys, os +#import sys +#import os # 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 @@ -26,7 +28,11 @@ import sys, os # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = [] +extensions = ['releases', 'sphinx.ext.autodoc', 'celery.contrib.sphinx'] + +# configuration for releases extension +releases_issue_uri = 'https://dev.gnuviech-server.de/gva/ticket/%s' +releases_release_uri = 'https://dev.gnuviech-server.de/gva/milestone/%s' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -171,21 +177,21 @@ htmlhelp_basename = 'gnuviechadmindoc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', -# Additional stuff for the LaTeX preamble. -#'preamble': '', + # Additional stuff for the LaTeX preamble. + #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'gnuviechadmin.tex', u'gnuviechadmin Documentation', - u'Jan Dittberner', 'manual'), + ('index', 'gnuviechadmin.tex', u'gnuviechadmin Documentation', + u'Jan Dittberner', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -228,9 +234,9 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'gnuviechadmin', u'gnuviechadmin Documentation', - u'Jan Dittberner', 'gnuviechadmin', 'Customer center for gnuviech servers.', - 'Miscellaneous'), + ('index', 'gnuviechadmin', u'gnuviechadmin Documentation', + u'Jan Dittberner', 'gnuviechadmin', 'Customer center for gnuviech servers.', + 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. diff --git a/docs/index.rst b/docs/index.rst index 99ec616..425bea4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -14,7 +14,7 @@ Contents: install deploy tests - + changelog Indices and tables diff --git a/requirements/local.txt b/requirements/local.txt index 644abf3..a81214d 100644 --- a/requirements/local.txt +++ b/requirements/local.txt @@ -3,3 +3,4 @@ coverage==3.7.1 django-debug-toolbar==1.2.1 Sphinx==1.2.2 +releases==0.6.1 From e8285518a3036c14b370432b70bd451efd71a403 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 00:32:06 +0200 Subject: [PATCH 12/48] fix small group handling bugs - use create_grouptaskresult instead of create for creating GroupTaskResult - use group.gid instead of non-existing group.id --- gnuviechadmin/osusers/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gnuviechadmin/osusers/models.py b/gnuviechadmin/osusers/models.py index 468ce31..7ae5174 100644 --- a/gnuviechadmin/osusers/models.py +++ b/gnuviechadmin/osusers/models.py @@ -174,7 +174,7 @@ class UserManager(models.Manager): password = generate_password() homedir = os.path.join(settings.OSUSER_HOME_BASEPATH, username) group = Group.objects.create(groupname=username, gid=gid) - GroupTaskResult.objects.create( + GroupTaskResult.objects.create_grouptaskresult( group, create_ldap_group.delay(group.groupname, group.gid, group.descr) ) @@ -212,7 +212,7 @@ class User(TimeStampedModel, models.Model): UserTaskResult.objects.create_usertaskresult( self, create_ldap_user.delay( - self.username, self.uid, self.group.id, self.gecos, + self.username, self.uid, self.group.gid, self.gecos, self.homedir, self.shell, password ), commit=True @@ -222,7 +222,7 @@ class User(TimeStampedModel, models.Model): UserTaskResult.objects.create_usertaskresult( self, create_ldap_user.delay( - self.username, self.uid, self.group.id, self.gecos, + self.username, self.uid, self.group.gid, self.gecos, self.homedir, self.shell, password=None ) ) From b9dd34d5277e6b12d0df042e4e0feda9d7655237 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 01:36:50 +0200 Subject: [PATCH 13/48] add initial osusers unit tests --- gnuviechadmin/osusers/models.py | 67 ++++++++++++++-------- gnuviechadmin/osusers/tests.py | 3 - gnuviechadmin/osusers/tests/__init__.py | 0 gnuviechadmin/osusers/tests/test_models.py | 54 +++++++++++++++++ 4 files changed, 97 insertions(+), 27 deletions(-) delete mode 100644 gnuviechadmin/osusers/tests.py create mode 100644 gnuviechadmin/osusers/tests/__init__.py create mode 100644 gnuviechadmin/osusers/tests/test_models.py diff --git a/gnuviechadmin/osusers/models.py b/gnuviechadmin/osusers/models.py index 7ae5174..b61dda3 100644 --- a/gnuviechadmin/osusers/models.py +++ b/gnuviechadmin/osusers/models.py @@ -25,6 +25,10 @@ from .tasks import ( ) +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) @@ -47,7 +51,7 @@ class TaskResult(TimeStampedModel, models.Model): def update_taskstatus(self): if not self.is_finished: - asyncresult = AsyncResult(self.task_uuid, task_name=self.task_name) + asyncresult = AsyncResult(self.task_uuid) self._set_result_fields(asyncresult) self.save() @@ -83,23 +87,26 @@ class Group(TimeStampedModel, models.Model): 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) + self, + create_ldap_group.delay(self.groupname, self.gid, self.descr), + 'create_ldap_group' ) return self 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.delay(self.groupname), + 'delete_ldap_group' ) super(Group, self).delete(*args, **kwargs) class TaskResultManager(models.Manager): - def create(self, asyncresult): + def create(self, asyncresult, task_name): result = self.model( - task_uuid=asyncresult.task_id, task_name=asyncresult.task_name + task_uuid=asyncresult.task_id, task_name=task_name ) result._set_result_fields(asyncresult) return result @@ -107,9 +114,11 @@ class TaskResultManager(models.Manager): class DeleteTaskResultManager(TaskResultManager): - def create_deletetaskresult(self, modeltype, modelname, asyncresult): + def create_deletetaskresult( + self, modeltype, modelname, asyncresult, task_name + ): taskresult = super(DeleteTaskResultManager, self).create( - asyncresult) + asyncresult, task_name) taskresult.modeltype = modeltype taskresult.modelname = modelname taskresult.save() @@ -126,9 +135,11 @@ class DeleteTaskResult(TaskResult): class GroupTaskResultManager(TaskResultManager): - def create_grouptaskresult(self, group, asyncresult, commit=False): + def create_grouptaskresult( + self, group, asyncresult, task_name, commit=False + ): taskresult = super(GroupTaskResultManager, self).create( - asyncresult) + asyncresult, task_name) taskresult.group = group taskresult.save() return taskresult @@ -176,7 +187,8 @@ class UserManager(models.Manager): group = Group.objects.create(groupname=username, gid=gid) GroupTaskResult.objects.create_grouptaskresult( group, - create_ldap_group.delay(group.groupname, group.gid, group.descr) + create_ldap_group.delay(group.groupname, group.gid, group.descr), + 'create_ldap_group' ) user = self.create(username=username, group=group, uid=uid, homedir=homedir, @@ -215,6 +227,7 @@ class User(TimeStampedModel, models.Model): self.username, self.uid, self.group.gid, self.gecos, self.homedir, self.shell, password ), + 'create_ldap_user', commit=True ) @@ -224,7 +237,8 @@ class User(TimeStampedModel, models.Model): create_ldap_user.delay( self.username, self.uid, self.group.gid, self.gecos, self.homedir, self.shell, password=None - ) + ), + 'create_ldap_user' ) return super(User, self).save(*args, **kwargs) @@ -236,15 +250,18 @@ class User(TimeStampedModel, models.Model): 'usergroup', '{0} in {1}'.format(self.username, group.groupname), remove_ldap_user_from_group.delay( - self.username, group.groupname) + 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.delay(self.username), + 'delete_ldap_user' ) DeleteTaskResult.objects.create_deletetaskresult( 'group', self.group.groupname, - delete_ldap_group_if_empty.delay(self.group.groupname) + delete_ldap_group_if_empty.delay(self.group.groupname), + 'delete_ldap_group_if_empty' ) self.group.delete() super(User, self).delete(*args, **kwargs) @@ -252,8 +269,10 @@ class User(TimeStampedModel, models.Model): class UserTaskResultManager(TaskResultManager): - def create_usertaskresult(self, user, asyncresult, commit=False): - taskresult = self.create(asyncresult) + def create_usertaskresult( + self, user, asyncresult, task_name, commit=False + ): + taskresult = self.create(asyncresult, task_name) taskresult.user = user taskresult.save() return taskresult @@ -336,15 +355,14 @@ class AdditionalGroup(TimeStampedModel, models.Model): def clean(self): if self.user.group == self.group: - raise ValidationError(_( - "You can not use a user's primary group.")) + raise ValidationError(CANNOT_USE_PRIMARY_GROUP_AS_ADDITIONAL) def save(self, *args, **kwargs): - res = add_ldap_user_to_group.delay( - self.user.username, self.group.groupname) - GroupTaskResult.objects.create( - group=self.group, task_uuid=res.id, - task_name=res.task_name + GroupTaskResult.objects.create_grouptaskresult( + self.group, + add_ldap_user_to_group.delay( + self.user.username, self.group.groupname), + 'add_ldap_user_to_group' ) super(AdditionalGroup, self).save(*args, **kwargs) @@ -353,7 +371,8 @@ class AdditionalGroup(TimeStampedModel, models.Model): 'usergroup', str(self), remove_ldap_user_from_group.delay( - self.user.username, self.group.groupname) + self.user.username, self.group.groupname), + 'remove_ldap_user_from_group' ) super(AdditionalGroup, self).delete(*args, **kwargs) diff --git a/gnuviechadmin/osusers/tests.py b/gnuviechadmin/osusers/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/gnuviechadmin/osusers/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/gnuviechadmin/osusers/tests/__init__.py b/gnuviechadmin/osusers/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gnuviechadmin/osusers/tests/test_models.py b/gnuviechadmin/osusers/tests/test_models.py new file mode 100644 index 0000000..da78b92 --- /dev/null +++ b/gnuviechadmin/osusers/tests/test_models.py @@ -0,0 +1,54 @@ +from django.core.exceptions import ValidationError +from django.test import TestCase +from django.test.utils import override_settings + +from osusers.models import ( + CANNOT_USE_PRIMARY_GROUP_AS_ADDITIONAL, + AdditionalGroup, + Group, + Shadow, + User, +) + + +class AdditionalGroupTest(TestCase): + @override_settings( + CELERY_ALWAYS_EAGER=True, + CELERY_CACHE_BACKEND='memory', + BROKER_BACKEND='memory' + ) + def test_clean_primary_group(self): + group1 = Group.objects.create(groupname='test1', gid=1000) + user = User.objects.create( + username='test', uid=1000, group=group1, + homedir='/home/test', shell='/bin/bash') + testsubj = AdditionalGroup(user=user, group=group1) + with self.assertRaises(ValidationError) as cm: + testsubj.clean() + self.assertEqual( + cm.exception.message, CANNOT_USE_PRIMARY_GROUP_AS_ADDITIONAL) + + @override_settings( + CELERY_ALWAYS_EAGER=True, + CELERY_CACHE_BACKEND='memory', + BROKER_BACKEND='memory' + ) + def test_clean_other_group(self): + group1 = Group(groupname='test1', gid=1000) + group2 = Group(groupname='test2', gid=1001) + user = User(username='test', uid=1000, group=group1, + homedir='/home/test', shell='/bin/bash') + testsubj = AdditionalGroup(user=user, group=group2) + testsubj.clean() + + +class GroupTest(TestCase): + pass + + +class ShadowTest(TestCase): + pass + + +class UserTest(TestCase): + pass From 8cec71fe1fa8ba0620aa8e8734ea48331a13e421 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 01:45:50 +0200 Subject: [PATCH 14/48] add test for osusers.models.Shadow.__str__ --- gnuviechadmin/osusers/tests/test_models.py | 36 +++++++++++++--------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/gnuviechadmin/osusers/tests/test_models.py b/gnuviechadmin/osusers/tests/test_models.py index da78b92..c6dcf5e 100644 --- a/gnuviechadmin/osusers/tests/test_models.py +++ b/gnuviechadmin/osusers/tests/test_models.py @@ -11,12 +11,16 @@ from osusers.models import ( ) -class AdditionalGroupTest(TestCase): - @override_settings( - CELERY_ALWAYS_EAGER=True, - CELERY_CACHE_BACKEND='memory', - BROKER_BACKEND='memory' - ) +@override_settings( + CELERY_ALWAYS_EAGER=True, + CELERY_CACHE_BACKEND='memory', + BROKER_BACKEND='memory' +) +class TestCaseWithCeleryTasks(TestCase): + pass + + +class AdditionalGroupTest(TestCaseWithCeleryTasks): def test_clean_primary_group(self): group1 = Group.objects.create(groupname='test1', gid=1000) user = User.objects.create( @@ -28,11 +32,6 @@ class AdditionalGroupTest(TestCase): self.assertEqual( cm.exception.message, CANNOT_USE_PRIMARY_GROUP_AS_ADDITIONAL) - @override_settings( - CELERY_ALWAYS_EAGER=True, - CELERY_CACHE_BACKEND='memory', - BROKER_BACKEND='memory' - ) def test_clean_other_group(self): group1 = Group(groupname='test1', gid=1000) group2 = Group(groupname='test2', gid=1001) @@ -42,13 +41,20 @@ class AdditionalGroupTest(TestCase): testsubj.clean() -class GroupTest(TestCase): +class GroupTest(TestCaseWithCeleryTasks): pass -class ShadowTest(TestCase): - pass +class ShadowTest(TestCaseWithCeleryTasks): + def test___str__(self): + group = Group.objects.create( + groupname='test', gid=1000) + user = User.objects.create( + username='test', uid=1000, group=group, homedir='/home/test', + shell='/bin/bash') + shadow = Shadow(user=user) + self.assertEqual(str(shadow), 'for user test (1000)') -class UserTest(TestCase): +class UserTest(TestCaseWithCeleryTasks): pass From 79ced4a7e7f5e07052c4727b04e8f247a8656dea Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 11:25:10 +0200 Subject: [PATCH 15/48] add mock to local and test requirements --- requirements/local.txt | 1 + requirements/test.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/requirements/local.txt b/requirements/local.txt index a81214d..df01fa9 100644 --- a/requirements/local.txt +++ b/requirements/local.txt @@ -1,6 +1,7 @@ # Local development dependencies go here -r base.txt coverage==3.7.1 +mock==1.0.1 django-debug-toolbar==1.2.1 Sphinx==1.2.2 releases==0.6.1 diff --git a/requirements/test.txt b/requirements/test.txt index bb78fde..8bf1098 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -1,3 +1,4 @@ # Test dependencies go here. -r base.txt coverage==3.7.1 +mock==1.0.1 From 7d9224db6344c6b2b985728a3b24649b41dc0556 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 11:25:30 +0200 Subject: [PATCH 16/48] implement tests for TaskResult - implement TaskResultTest and TaskResultManagerTest --- gnuviechadmin/osusers/tests/test_models.py | 67 ++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/gnuviechadmin/osusers/tests/test_models.py b/gnuviechadmin/osusers/tests/test_models.py index c6dcf5e..0d0019e 100644 --- a/gnuviechadmin/osusers/tests/test_models.py +++ b/gnuviechadmin/osusers/tests/test_models.py @@ -2,9 +2,12 @@ from django.core.exceptions import ValidationError from django.test import TestCase from django.test.utils import override_settings +from mock import patch, MagicMock + from osusers.models import ( CANNOT_USE_PRIMARY_GROUP_AS_ADDITIONAL, AdditionalGroup, + DeleteTaskResult, Group, Shadow, User, @@ -56,5 +59,69 @@ class ShadowTest(TestCaseWithCeleryTasks): self.assertEqual(str(shadow), 'for user test (1000)') +TEST_TASK_UUID = '3120f6a8-2665-4fa3-a785-79efd28bfe92' +TEST_TASK_NAME = 'test.task' +TEST_TASK_RESULT = '4ll y0ur b453 4r3 b3l0ng t0 u5' + + +class TaskResultTest(TestCase): + def test__set_result_fields_not_ready(self): + mock = MagicMock(task_id=TEST_TASK_UUID, task_name=TEST_TASK_NAME) + mock.ready.return_value = False + tr = DeleteTaskResult.objects.create(mock, TEST_TASK_NAME) + self.assertFalse(tr.is_finished) + self.assertFalse(tr.is_success) + self.assertEqual(tr.state, '') + self.assertEqual(tr.result_body, '') + + def test__set_result_fields_ready(self): + mock = MagicMock(task_id=TEST_TASK_UUID, task_name=TEST_TASK_NAME, + state='SUCCESS', result=TEST_TASK_RESULT) + mock.ready.return_value = True + tr = DeleteTaskResult.objects.create(mock, TEST_TASK_NAME) + self.assertTrue(tr.is_finished) + self.assertTrue(tr.is_success) + self.assertEqual(tr.state, 'SUCCESS') + self.assertEqual(tr.result_body, TEST_TASK_RESULT) + + def test__set_result_fields_exception(self): + mock = MagicMock(task_id=TEST_TASK_UUID, task_name=TEST_TASK_NAME, + state='FAILURE', result=Exception('Fail')) + mock.ready.return_value = True + tr = DeleteTaskResult.objects.create(mock, TEST_TASK_NAME) + self.assertTrue(tr.is_finished) + self.assertFalse(tr.is_success) + self.assertEqual(tr.state, 'FAILURE') + self.assertEqual(tr.result_body, 'Fail') + + @patch('osusers.models.AsyncResult') + def test_update_taskstatus_unfinished(self, asyncres): + mock = MagicMock(task_id=TEST_TASK_UUID, task_name=TEST_TASK_NAME) + mock.ready.return_value = False + tr = DeleteTaskResult.objects.create(mock, TEST_TASK_NAME) + self.assertFalse(tr.is_finished) + mymock = asyncres(TEST_TASK_UUID) + mymock.ready.return_value = True + mymock.state = 'SUCCESS' + mymock.result = TEST_RESULT + tr.update_taskstatus() + mymock.ready.assert_called_with() + self.assertTrue(tr.is_finished) + + +TEST_RESULT = MagicMock() +TEST_RESULT.task_id = TEST_TASK_UUID +TEST_RESULT.task_name = TEST_TASK_NAME +TEST_RESULT.ready.return_value = False + + +class TaskResultManagerTest(TestCase): + def test_create(self): + tr = DeleteTaskResult.objects.create(TEST_RESULT, TEST_TASK_NAME) + self.assertIsInstance(tr, DeleteTaskResult) + self.assertEqual(tr.task_uuid, TEST_TASK_UUID) + self.assertEqual(tr.task_name, TEST_TASK_NAME) + + class UserTest(TestCaseWithCeleryTasks): pass From c45e93be03c89da3d8140491f7d9c2d7b4c6187b Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 11:56:42 +0200 Subject: [PATCH 17/48] add tests for Group and GroupManager --- gnuviechadmin/osusers/tests/test_models.py | 36 +++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/gnuviechadmin/osusers/tests/test_models.py b/gnuviechadmin/osusers/tests/test_models.py index 0d0019e..88f8ea5 100644 --- a/gnuviechadmin/osusers/tests/test_models.py +++ b/gnuviechadmin/osusers/tests/test_models.py @@ -9,6 +9,7 @@ from osusers.models import ( AdditionalGroup, DeleteTaskResult, Group, + GroupTaskResult, Shadow, User, ) @@ -44,8 +45,41 @@ class AdditionalGroupTest(TestCaseWithCeleryTasks): testsubj.clean() +@override_settings(OSUSER_MINGID=10000) +class GroupManagerTest(TestCase): + def test_get_next_gid_first(self): + self.assertEqual(Group.objects.get_next_gid(), 10000) + + def test_get_next_gid_second(self): + Group.objects.create(gid=10010, groupname='test') + self.assertEqual(Group.objects.get_next_gid(), 10011) + + class GroupTest(TestCaseWithCeleryTasks): - pass + def test___str__(self): + group = Group.objects.create(gid=10000, groupname='test') + self.assertEqual(str(group), 'test (10000)') + + def test_save(self): + group = Group(gid=10000, groupname='test') + self.assertIs(group.save(), group) + taskres = GroupTaskResult.objects.all() + self.assertEqual(len(taskres), 1) + self.assertEqual(taskres[0].group, group) + self.assertEqual(taskres[0].task_name, 'create_ldap_group') + + def test_delete(self): + group = Group.objects.create(gid=10000, groupname='test') + self.assertEqual(len(Group.objects.all()), 1) + self.assertEqual(len(GroupTaskResult.objects.all()), 1) + group.delete() + self.assertEqual(len(Group.objects.all()), 0) + self.assertEqual(len(GroupTaskResult.objects.all()), 0) + taskres = DeleteTaskResult.objects.all() + self.assertEqual(len(taskres), 1) + self.assertEqual(taskres[0].task_name, 'delete_ldap_group') + self.assertEqual(taskres[0].modeltype, 'group') + self.assertEqual(taskres[0].modelname, 'test') class ShadowTest(TestCaseWithCeleryTasks): From d82146987eb6183290d11290354ac3c7bd865a89 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 12:07:18 +0200 Subject: [PATCH 18/48] update changelog, document test process - fix headline in index.rst - add release 0.1 in changelog and add two more features - add tests.rst documenting how to run the test suite and how to check test coverage --- docs/changelog.rst | 6 ++++++ docs/index.rst | 2 +- docs/tests.rst | 26 ++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 docs/tests.rst diff --git a/docs/changelog.rst b/docs/changelog.rst index 9826f7d..c3bff99 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,5 +1,11 @@ Changelog ========= +* :feature:`-` initial test suite for models +* :feature:`-` `Celery `_ integration for ldap + synchronization + +* :release:`0.1 <2014-05-25>` * :feature:`-` initial model code for os users * :feature:`-` initial model code for mail address and mailbox management +* :feature:`-` initial model code for domains diff --git a/docs/index.rst b/docs/index.rst index 425bea4..0d9e0c2 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -4,7 +4,7 @@ contain the root `toctree` directive. Welcome to gnuviechadmin's documentation! -==================================== +========================================= Contents: diff --git a/docs/tests.rst b/docs/tests.rst new file mode 100644 index 0000000..b56edd1 --- /dev/null +++ b/docs/tests.rst @@ -0,0 +1,26 @@ +Tests +===== + +To run the tests you can just use the :program:`manage.py` script: + +.. code-block:: sh + + $ python manage.py test + +Coverage +-------- + +To capture test coverage information you can run: + +.. code-block:: sh + + $ coverage run --branch manage.py test + +To view the coverage data use: + +.. code-block:: sh + + $ coverage report -m + +The coverage configuration is in :file:`.coveragerc`. Add new apps to the +`source` configuration in the `[run]` section of that configuration file. From df9800b827c1a5327b99ee4d3c9b9a98f4585a81 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 14:43:42 +0200 Subject: [PATCH 19/48] add tests for osusers.models.UserManager --- gnuviechadmin/osusers/tests/test_models.py | 97 ++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/gnuviechadmin/osusers/tests/test_models.py b/gnuviechadmin/osusers/tests/test_models.py index 88f8ea5..2dc57f3 100644 --- a/gnuviechadmin/osusers/tests/test_models.py +++ b/gnuviechadmin/osusers/tests/test_models.py @@ -4,6 +4,8 @@ from django.test.utils import override_settings from mock import patch, MagicMock +from passlib.hash import sha512_crypt + from osusers.models import ( CANNOT_USE_PRIMARY_GROUP_AS_ADDITIONAL, AdditionalGroup, @@ -157,5 +159,100 @@ class TaskResultManagerTest(TestCase): self.assertEqual(tr.task_name, TEST_TASK_NAME) +@override_settings( + OSUSER_MINUID=10000, OSUSER_MINGID=10000, OSUSER_USERNAME_PREFIX='test', + OSUSER_HOME_BASEPATH='/home', OSUSER_DEFAULT_SHELL='/bin/fooshell' +) +class UserManagerTest(TestCaseWithCeleryTasks): + def _create_group(self): + return Group.objects.create(gid=10000, groupname='foo') + + def test_get_next_uid_first(self): + self.assertEqual(User.objects.get_next_uid(), 10000) + + def test_get_next_uid_second(self): + User.objects.create( + uid=10010, username='foo', group=self._create_group(), + homedir='/home/foo', shell='/bin/fooshell') + self.assertEqual(User.objects.get_next_uid(), 10011) + + def test_get_next_username_first(self): + self.assertEqual(User.objects.get_next_username(), 'test01') + + def test_get_next_username_second(self): + User.objects.create( + uid=10000, username='test01', group=self._create_group(), + homedir='/home/foo', shell='/bin/fooshell') + self.assertEqual(User.objects.get_next_username(), 'test02') + + def test_get_next_username_gaps(self): + group = self._create_group() + User.objects.create( + uid=10000, username='test01', group=group, + homedir='/home/foo', shell='/bin/fooshell') + User.objects.create( + uid=10002, username='test03', group=group, + homedir='/home/foo', shell='/bin/fooshell') + self.assertEqual(User.objects.get_next_username(), 'test02') + + def test_create_user_first(self): + user = User.objects.create_user() + self.assertIsInstance(user, User) + self.assertEqual(user.uid, 10000) + self.assertEqual(user.group.gid, 10000) + self.assertEqual(user.group.groupname, 'test01') + self.assertEqual(user.username, 'test01') + self.assertEqual(user.homedir, '/home/test01') + self.assertEqual(user.shell, '/bin/fooshell') + self.assertIsNotNone(user.shadow) + + def test_create_user_second(self): + User.objects.create_user() + user = User.objects.create_user() + self.assertIsInstance(user, User) + self.assertEqual(user.uid, 10001) + self.assertEqual(user.group.gid, 10001) + self.assertEqual(user.group.groupname, 'test02') + self.assertEqual(user.username, 'test02') + self.assertEqual(user.homedir, '/home/test02') + self.assertEqual(user.shell, '/bin/fooshell') + self.assertIsNotNone(user.shadow) + self.assertEqual(len(User.objects.all()), 2) + + def test_create_user_known_password(self): + user = User.objects.create_user(password='foobar') + self.assertIsInstance(user, User) + self.assertEqual(user.uid, 10000) + self.assertEqual(user.group.gid, 10000) + self.assertEqual(user.group.groupname, 'test01') + self.assertEqual(user.username, 'test01') + self.assertEqual(user.homedir, '/home/test01') + self.assertEqual(user.shell, '/bin/fooshell') + self.assertIsNotNone(user.shadow) + self.assertTrue(sha512_crypt.verify('foobar', user.shadow.passwd)) + + def test_create_user_predefined_username(self): + user = User.objects.create_user(username='tester') + self.assertIsInstance(user, User) + self.assertEqual(user.uid, 10000) + self.assertEqual(user.group.gid, 10000) + self.assertEqual(user.group.groupname, 'tester') + self.assertEqual(user.username, 'tester') + self.assertEqual(user.homedir, '/home/tester') + self.assertEqual(user.shell, '/bin/fooshell') + self.assertIsNotNone(user.shadow) + + def test_create_user_commit(self): + user = User.objects.create_user(commit=True) + self.assertIsInstance(user, User) + self.assertEqual(user.uid, 10000) + self.assertEqual(user.group.gid, 10000) + self.assertEqual(user.group.groupname, 'test01') + self.assertEqual(user.username, 'test01') + self.assertEqual(user.homedir, '/home/test01') + self.assertEqual(user.shell, '/bin/fooshell') + self.assertIsNotNone(user.shadow) + + class UserTest(TestCaseWithCeleryTasks): pass From 83562ba2bf2b106f1b8700113b80ec81c39e6336 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 14:51:33 +0200 Subject: [PATCH 20/48] implement test for User.set_password, add Shadow.set_password --- gnuviechadmin/osusers/models.py | 8 ++++++-- gnuviechadmin/osusers/tests/test_models.py | 21 ++++++++++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/gnuviechadmin/osusers/models.py b/gnuviechadmin/osusers/models.py index b61dda3..e586c66 100644 --- a/gnuviechadmin/osusers/models.py +++ b/gnuviechadmin/osusers/models.py @@ -221,6 +221,7 @@ class User(TimeStampedModel, models.Model): return '{0} ({1})'.format(self.username, self.uid) def set_password(self, password): + self.shadow.set_password(password) UserTaskResult.objects.create_usertaskresult( self, create_ldap_user.delay( @@ -289,12 +290,12 @@ class ShadowManager(models.Manager): def create_shadow(self, user, password): changedays = (timezone.now().date() - date(1970, 1, 1)).days - pwhash = sha512_crypt.encrypt(password) shadow = self.create( user=user, changedays=changedays, minage=0, maxage=None, gracedays=7, - inactdays=30, expiredays=None, passwd=pwhash + inactdays=30, expiredays=None ) + shadow.set_password(password) shadow.save() return shadow @@ -342,6 +343,9 @@ class Shadow(TimeStampedModel, models.Model): def __str__(self): return 'for user {0}'.format(self.user) + def set_password(self, password): + self.passwd = sha512_crypt.encrypt(password) + @python_2_unicode_compatible class AdditionalGroup(TimeStampedModel, models.Model): diff --git a/gnuviechadmin/osusers/tests/test_models.py b/gnuviechadmin/osusers/tests/test_models.py index 2dc57f3..bd198fd 100644 --- a/gnuviechadmin/osusers/tests/test_models.py +++ b/gnuviechadmin/osusers/tests/test_models.py @@ -14,6 +14,7 @@ from osusers.models import ( GroupTaskResult, Shadow, User, + UserTaskResult, ) @@ -254,5 +255,23 @@ class UserManagerTest(TestCaseWithCeleryTasks): self.assertIsNotNone(user.shadow) +@override_settings( + OSUSER_MINUID=10000, OSUSER_MINGID=10000, OSUSER_USERNAME_PREFIX='test', + OSUSER_HOME_BASEPATH='/home', OSUSER_DEFAULT_SHELL='/bin/fooshell' +) class UserTest(TestCaseWithCeleryTasks): - pass + + def test___str__(self): + user = User.objects.create_user() + self.assertEqual(str(user), 'test01 (10000)') + + def test_set_password(self): + user = User.objects.create_user() + self.assertFalse(sha512_crypt.verify('test', user.shadow.passwd)) + UserTaskResult.objects.all().delete() + user.set_password('test') + self.assertTrue(sha512_crypt.verify('test', user.shadow.passwd)) + taskres = UserTaskResult.objects.all() + self.assertEqual(len(taskres), 1) + self.assertEqual(taskres[0].user, user) + self.assertEqual(taskres[0].task_name, 'create_ldap_user') From ceeffb6d1c60f5e3efbd2a5a6f2ec7649a7a9845 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 15:03:15 +0200 Subject: [PATCH 21/48] refactor UserManager.create_user - remove duplicate create_ldap_group task - create Shadow in User.set_password if necessary - add test UserManagerTest.test_create_user_tasks for group task --- gnuviechadmin/osusers/models.py | 13 ++++++------- gnuviechadmin/osusers/tests/test_models.py | 7 +++++++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/gnuviechadmin/osusers/models.py b/gnuviechadmin/osusers/models.py index e586c66..a18b46f 100644 --- a/gnuviechadmin/osusers/models.py +++ b/gnuviechadmin/osusers/models.py @@ -185,15 +185,9 @@ class UserManager(models.Manager): password = generate_password() homedir = os.path.join(settings.OSUSER_HOME_BASEPATH, username) group = Group.objects.create(groupname=username, gid=gid) - GroupTaskResult.objects.create_grouptaskresult( - group, - create_ldap_group.delay(group.groupname, group.gid, group.descr), - 'create_ldap_group' - ) user = self.create(username=username, group=group, uid=uid, homedir=homedir, shell=settings.OSUSER_DEFAULT_SHELL) - Shadow.objects.create_shadow(user=user, password=password) user.set_password(password) if commit: user.save() @@ -221,7 +215,12 @@ class User(TimeStampedModel, models.Model): return '{0} ({1})'.format(self.username, self.uid) def set_password(self, password): - self.shadow.set_password(password) + if hasattr(self, 'shadow'): + self.shadow.set_password(password) + else: + self.shadow = Shadow.objects.create_shadow( + user=self, password=password + ) UserTaskResult.objects.create_usertaskresult( self, create_ldap_user.delay( diff --git a/gnuviechadmin/osusers/tests/test_models.py b/gnuviechadmin/osusers/tests/test_models.py index bd198fd..b2acc7a 100644 --- a/gnuviechadmin/osusers/tests/test_models.py +++ b/gnuviechadmin/osusers/tests/test_models.py @@ -207,6 +207,13 @@ class UserManagerTest(TestCaseWithCeleryTasks): self.assertEqual(user.shell, '/bin/fooshell') self.assertIsNotNone(user.shadow) + def test_create_user_tasks(self): + user = User.objects.create_user() + gtaskres = GroupTaskResult.objects.all() + self.assertEqual(len(gtaskres), 1) + self.assertEqual(gtaskres[0].task_name, 'create_ldap_group') + self.assertEqual(gtaskres[0].group, user.group) + def test_create_user_second(self): User.objects.create_user() user = User.objects.create_user() From b37e44ccfd9dea1a579f5b181aa251d3b895b824 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 15:26:01 +0200 Subject: [PATCH 22/48] add tests for osusers.models.User - fix task name in Group.delete - remove duplicate task from User.delete - adapt osusers.tests.test_models.GroupTest.test_delete - add tests to UserTest - test_save - test_delete_only_user - test_delete_additional_groups --- gnuviechadmin/osusers/models.py | 7 +--- gnuviechadmin/osusers/tests/test_models.py | 40 +++++++++++++++++++++- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/gnuviechadmin/osusers/models.py b/gnuviechadmin/osusers/models.py index a18b46f..6ad01b9 100644 --- a/gnuviechadmin/osusers/models.py +++ b/gnuviechadmin/osusers/models.py @@ -97,7 +97,7 @@ class Group(TimeStampedModel, models.Model): DeleteTaskResult.objects.create_deletetaskresult( 'group', self.groupname, delete_ldap_group_if_empty.delay(self.groupname), - 'delete_ldap_group' + 'delete_ldap_group_if_empty' ) super(Group, self).delete(*args, **kwargs) @@ -258,11 +258,6 @@ class User(TimeStampedModel, models.Model): delete_ldap_user.delay(self.username), 'delete_ldap_user' ) - DeleteTaskResult.objects.create_deletetaskresult( - 'group', self.group.groupname, - delete_ldap_group_if_empty.delay(self.group.groupname), - 'delete_ldap_group_if_empty' - ) self.group.delete() super(User, self).delete(*args, **kwargs) diff --git a/gnuviechadmin/osusers/tests/test_models.py b/gnuviechadmin/osusers/tests/test_models.py index b2acc7a..cf452ee 100644 --- a/gnuviechadmin/osusers/tests/test_models.py +++ b/gnuviechadmin/osusers/tests/test_models.py @@ -80,7 +80,8 @@ class GroupTest(TestCaseWithCeleryTasks): self.assertEqual(len(GroupTaskResult.objects.all()), 0) taskres = DeleteTaskResult.objects.all() self.assertEqual(len(taskres), 1) - self.assertEqual(taskres[0].task_name, 'delete_ldap_group') + self.assertEqual(taskres[0].task_name, + 'delete_ldap_group_if_empty') self.assertEqual(taskres[0].modeltype, 'group') self.assertEqual(taskres[0].modelname, 'test') @@ -282,3 +283,40 @@ class UserTest(TestCaseWithCeleryTasks): self.assertEqual(len(taskres), 1) self.assertEqual(taskres[0].user, user) self.assertEqual(taskres[0].task_name, 'create_ldap_user') + + def test_save(self): + user = User.objects.create_user() + UserTaskResult.objects.all().delete() + user.save() + taskres = UserTaskResult.objects.all() + self.assertEqual(len(taskres), 1) + self.assertEqual(taskres[0].user, user) + self.assertEqual(taskres[0].task_name, 'create_ldap_user') + + def test_delete_only_user(self): + user = User.objects.create_user() + user.delete() + taskres = DeleteTaskResult.objects.all() + self.assertEqual(len(taskres), 2) + self.assertIn('delete_ldap_user', + [r.task_name for r in taskres]) + self.assertIn('delete_ldap_group_if_empty', + [r.task_name for r in taskres]) + self.assertEqual(len(User.objects.all()), 0) + + def test_delete_additional_groups(self): + group1 = Group.objects.create(gid=2000, groupname='group1') + group2 = Group.objects.create(gid=2001, groupname='group2') + user = User.objects.create_user() + for group in [group1, group2]: + user.additionalgroup_set.add( + AdditionalGroup.objects.create(user=user, group=group)) + user.delete() + taskres = DeleteTaskResult.objects.all() + self.assertEqual(len(taskres), 4) + tasknames = [t.task_name for t in taskres] + self.assertEqual(tasknames.count('remove_ldap_user_from_group'), 2) + self.assertEqual(tasknames.count('delete_ldap_user'), 1) + self.assertEqual(tasknames.count('delete_ldap_group_if_empty'), 1) + self.assertEqual(len(User.objects.all()), 0) + self.assertEqual(len(AdditionalGroup.objects.all()), 0) From 20f5686d85d9863ddbec0e2461f40a551f5e606c Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 15:42:32 +0200 Subject: [PATCH 23/48] add ShadowManagerTest --- gnuviechadmin/osusers/tests/test_models.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/gnuviechadmin/osusers/tests/test_models.py b/gnuviechadmin/osusers/tests/test_models.py index cf452ee..9f78198 100644 --- a/gnuviechadmin/osusers/tests/test_models.py +++ b/gnuviechadmin/osusers/tests/test_models.py @@ -1,6 +1,9 @@ +from datetime import date + from django.core.exceptions import ValidationError from django.test import TestCase from django.test.utils import override_settings +from django.utils import timezone from mock import patch, MagicMock @@ -86,6 +89,24 @@ class GroupTest(TestCaseWithCeleryTasks): self.assertEqual(taskres[0].modelname, 'test') +class ShadowManagerTest(TestCaseWithCeleryTasks): + def test_create_shadow(self): + user = User( + username='test', uid=1000, + group=Group(gid=1000, groupname='test'), + homedir='/home/test', shell='/bin/fooshell') + shadow = Shadow.objects.create_shadow(user, 'test') + self.assertTrue(sha512_crypt.verify('test', shadow.passwd)) + self.assertEqual(shadow.changedays, + (timezone.now().date() - date(1970, 1, 1)).days) + self.assertEqual(shadow.user, user) + self.assertEqual(shadow.minage, 0) + self.assertIsNone(shadow.maxage) + self.assertEqual(shadow.gracedays, 7) + self.assertEqual(shadow.inactdays, 30) + self.assertIsNone(shadow.expiredays) + + class ShadowTest(TestCaseWithCeleryTasks): def test___str__(self): group = Group.objects.create( From c270a6087e4902d6dea988890cb60cd6bf61b0f9 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 16:09:30 +0200 Subject: [PATCH 24/48] add osusers.tests.test_models.ShadowTest --- gnuviechadmin/osusers/tests/test_models.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/gnuviechadmin/osusers/tests/test_models.py b/gnuviechadmin/osusers/tests/test_models.py index 9f78198..74d7ac3 100644 --- a/gnuviechadmin/osusers/tests/test_models.py +++ b/gnuviechadmin/osusers/tests/test_models.py @@ -117,6 +117,16 @@ class ShadowTest(TestCaseWithCeleryTasks): shadow = Shadow(user=user) self.assertEqual(str(shadow), 'for user test (1000)') + def test_set_password(self): + group = Group.objects.create( + groupname='test', gid=1000) + user = User.objects.create( + username='test', uid=1000, group=group, homedir='/home/test', + shell='/bin/bash') + shadow = Shadow(user=user) + shadow.set_password('test') + self.assertTrue(sha512_crypt.verify('test', shadow.passwd)) + TEST_TASK_UUID = '3120f6a8-2665-4fa3-a785-79efd28bfe92' TEST_TASK_NAME = 'test.task' From 842e207acceff193bef7037d3ac4c23bb8bfbd4b Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 16:30:14 +0200 Subject: [PATCH 25/48] complete coverage for osusers.models - add tests for AdditionalGroups methods save, delete and __str__ - add TaskResultTest.test_update_taskstatus_finished --- gnuviechadmin/osusers/tests/test_models.py | 59 ++++++++++++++++++---- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/gnuviechadmin/osusers/tests/test_models.py b/gnuviechadmin/osusers/tests/test_models.py index 74d7ac3..572cfcb 100644 --- a/gnuviechadmin/osusers/tests/test_models.py +++ b/gnuviechadmin/osusers/tests/test_models.py @@ -31,25 +31,51 @@ class TestCaseWithCeleryTasks(TestCase): class AdditionalGroupTest(TestCaseWithCeleryTasks): - def test_clean_primary_group(self): - group1 = Group.objects.create(groupname='test1', gid=1000) - user = User.objects.create( - username='test', uid=1000, group=group1, + def setUp(self): + self.group1 = Group.objects.create(groupname='test1', gid=1000) + self.user = User.objects.create( + username='test', uid=1000, group=self.group1, homedir='/home/test', shell='/bin/bash') - testsubj = AdditionalGroup(user=user, group=group1) + + def test_clean_primary_group(self): + testsubj = AdditionalGroup(user=self.user, group=self.group1) with self.assertRaises(ValidationError) as cm: testsubj.clean() self.assertEqual( cm.exception.message, CANNOT_USE_PRIMARY_GROUP_AS_ADDITIONAL) def test_clean_other_group(self): - group1 = Group(groupname='test1', gid=1000) - group2 = Group(groupname='test2', gid=1001) - user = User(username='test', uid=1000, group=group1, - homedir='/home/test', shell='/bin/bash') - testsubj = AdditionalGroup(user=user, group=group2) + group2 = Group.objects.create(groupname='test2', gid=1001) + testsubj = AdditionalGroup(user=self.user, group=group2) testsubj.clean() + def test_save(self): + group2 = Group.objects.create(groupname='test2', gid=1001) + GroupTaskResult.objects.all().delete() + addgroup = AdditionalGroup(user=self.user, group=group2) + addgroup.save() + taskres = GroupTaskResult.objects.all() + self.assertTrue(len(taskres), 1) + self.assertEqual(taskres[0].task_name, 'add_ldap_user_to_group') + self.assertEqual(taskres[0].group, group2) + + def test_delete(self): + group2 = Group.objects.create(groupname='test2', gid=1001) + addgroup = AdditionalGroup.objects.create(user=self.user, group=group2) + DeleteTaskResult.objects.all().delete() + addgroup.delete() + taskres = DeleteTaskResult.objects.all() + self.assertTrue(len(taskres), 1) + self.assertEqual(taskres[0].task_name, 'remove_ldap_user_from_group') + self.assertEqual(taskres[0].modeltype, 'usergroup') + self.assertEqual(taskres[0].modelname, 'test (1000) in test2 (1001)') + self.assertEqual(len(AdditionalGroup.objects.all()), 0) + + def test___str__(self): + group2 = Group.objects.create(groupname='test2', gid=1001) + addgroup = AdditionalGroup.objects.create(user=self.user, group=group2) + self.assertEqual(str(addgroup), 'test (1000) in test2 (1001)') + @override_settings(OSUSER_MINGID=10000) class GroupManagerTest(TestCase): @@ -177,6 +203,19 @@ class TaskResultTest(TestCase): mymock.ready.assert_called_with() self.assertTrue(tr.is_finished) + @patch('osusers.models.AsyncResult') + def test_update_taskstatus_finished(self, asyncres): + mock = MagicMock(task_id=TEST_TASK_UUID, task_name=TEST_TASK_NAME) + mock.ready.return_value = True + mock.state = 'SUCCESS' + mock.result = TEST_RESULT + tr = DeleteTaskResult.objects.create(mock, TEST_TASK_NAME) + self.assertTrue(tr.is_finished) + mymock = asyncres(TEST_TASK_UUID) + tr.update_taskstatus() + self.assertFalse(mymock.ready.called) + self.assertTrue(tr.is_finished) + TEST_RESULT = MagicMock() TEST_RESULT.task_id = TEST_TASK_UUID From 6a931ad21ce06fb220ca50615a2ce9d311c2e740 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 16:44:42 +0200 Subject: [PATCH 26/48] add test for domains admin --- gnuviechadmin/domains/tests/test_admin.py | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 gnuviechadmin/domains/tests/test_admin.py diff --git a/gnuviechadmin/domains/tests/test_admin.py b/gnuviechadmin/domains/tests/test_admin.py new file mode 100644 index 0000000..aa717a9 --- /dev/null +++ b/gnuviechadmin/domains/tests/test_admin.py @@ -0,0 +1,8 @@ +from django.test import TestCase +from django.core.urlresolvers import reverse + + +class TestMailDomainAdmin(TestCase): + def test_admin_for_maildomain(self): + admin_url = reverse('admin:domains_maildomain_changelist') + self.assertIsNotNone(admin_url) From 33e7fb29af0792b7edca695ebd4aa1cf0335e6e2 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 16:45:37 +0200 Subject: [PATCH 27/48] remove empty domains.views --- gnuviechadmin/domains/views.py | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 gnuviechadmin/domains/views.py diff --git a/gnuviechadmin/domains/views.py b/gnuviechadmin/domains/views.py deleted file mode 100644 index 91ea44a..0000000 --- a/gnuviechadmin/domains/views.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.shortcuts import render - -# Create your views here. From 6197728330184f26b3f276d1b54677216748c3fd Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 16:47:37 +0200 Subject: [PATCH 28/48] update changelog --- docs/changelog.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index c3bff99..d13de71 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,7 +1,8 @@ Changelog ========= -* :feature:`-` initial test suite for models +* :feature:`-` full test suite for domains app +* :feature:`-` full test suite for osusers.models * :feature:`-` `Celery `_ integration for ldap synchronization From 3407a5ed244216a872e8bb1620ccdc0e971f19b5 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 18:25:30 +0200 Subject: [PATCH 29/48] add tests for managemails.admin --- gnuviechadmin/managemails/tests/test_admin.py | 189 ++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 gnuviechadmin/managemails/tests/test_admin.py diff --git a/gnuviechadmin/managemails/tests/test_admin.py b/gnuviechadmin/managemails/tests/test_admin.py new file mode 100644 index 0000000..fbe00b7 --- /dev/null +++ b/gnuviechadmin/managemails/tests/test_admin.py @@ -0,0 +1,189 @@ +from django import forms +from django.core.urlresolvers import reverse +from django.test import TestCase +from django.test.utils import override_settings +from django.utils.html import format_html +from django.utils.translation import ugettext as _ + +from django.contrib.admin import AdminSite + +from mock import Mock + +from osusers.models import User + +from managemails.admin import ( + ActivationChangeMixin, + MailboxAdmin, + MailboxChangeForm, + MailboxCreationForm, + PASSWORD_MISMATCH_ERROR, + ReadOnlyPasswordHashField, + ReadOnlyPasswordHashWidget, +) +from managemails.models import ( + Mailbox, +) + + +class TestReadOnlyPasswordHashWidget(TestCase): + def test_render(self): + widget = ReadOnlyPasswordHashWidget() + rendered = widget.render('password', 'secret', {'class': 'test'}) + self.assertEqual( + rendered, + format_html( + '
{0}
', + format_html('{0}: secret ', + _('Hash')) + )) + + +class TestReadOnlyPasswordHashField(TestCase): + def test___init__(self): + field = ReadOnlyPasswordHashField() + self.assertFalse(field.required) + + def test_bound_data(self): + field = ReadOnlyPasswordHashField() + self.assertEqual(field.bound_data('new', 'old'), 'old') + + def test__has_changed(self): + field = ReadOnlyPasswordHashField() + self.assertFalse(field._has_changed('new', 'old')) + + +class TestMailboxCreationForm(TestCase): + def test_clean_password2_same(self): + form = MailboxCreationForm() + form.cleaned_data = {'password1': 'secret', 'password2': 'secret'} + self.assertEqual(form.clean_password2(), 'secret') + + def test_clean_password2_empty(self): + form = MailboxCreationForm() + form.cleaned_data = {} + self.assertIsNone(form.clean_password2()) + + def test_clean_password2_mismatch(self): + form = MailboxCreationForm() + form.cleaned_data = {'password1': 'secretx', 'password2': 'secrety'} + with self.assertRaises(forms.ValidationError) as cm: + form.clean_password2() + self.assertEqual(cm.exception.message, PASSWORD_MISMATCH_ERROR) + + @override_settings( + CELERY_ALWAYS_EAGER=True, + CELERY_CACHE_BACKEND='memory', + BROKER_BACKEND='memory' + ) + def test_save_commit(self): + user = User.objects.create_user() + form = MailboxCreationForm(data={ + 'osuser': user.uid, + 'password1': 'secret', + 'password2': 'secret', + }) + mailbox = form.save() + self.assertIsNotNone(mailbox) + self.assertEqual( + len(Mailbox.objects.filter(osuser=user)), 1) + + @override_settings( + CELERY_ALWAYS_EAGER=True, + CELERY_CACHE_BACKEND='memory', + BROKER_BACKEND='memory' + ) + def test_save_no_commit(self): + user = User.objects.create_user() + form = MailboxCreationForm(data={ + 'osuser': user.uid, + 'password1': 'secret', + 'password2': 'secret', + }) + mailbox = form.save(commit=False) + self.assertIsNotNone(mailbox) + self.assertEqual( + len(Mailbox.objects.filter(osuser=user)), 0) + + +class TestMailboxChangeForm(TestCase): + @override_settings( + CELERY_ALWAYS_EAGER=True, + CELERY_CACHE_BACKEND='memory', + BROKER_BACKEND='memory' + ) + def test_clean_password(self): + mailbox = Mailbox(username='test', osuser=User.objects.create_user()) + mailbox.set_password('test') + mailbox.save() + form = MailboxChangeForm(instance=mailbox, data={'password': 'blub'}) + self.assertEqual(form.clean_password(), mailbox.password) + + +class TestActivationChangeMixin(TestCase): + def test_activate(self): + querysetmock = Mock() + activationchange = ActivationChangeMixin() + activationchange.activate(Mock(), querysetmock) + querysetmock.update.called_with(active=True) + + def test_deactivate(self): + querysetmock = Mock() + activationchange = ActivationChangeMixin() + activationchange.deactivate(Mock(), querysetmock) + querysetmock.update.called_with(active=False) + + +class TestMailBoxAdmin(TestCase): + def setUp(self): + site = AdminSite() + self.mbadmin = MailboxAdmin(Mailbox, site) + + def test_get_fieldsets_without_object(self): + self.assertEqual( + self.mbadmin.get_fieldsets(Mock()), + self.mbadmin.add_fieldsets) + + @override_settings( + CELERY_ALWAYS_EAGER=True, + CELERY_CACHE_BACKEND='memory', + BROKER_BACKEND='memory' + ) + def test_get_fieldsets_with_object(self): + mailbox = Mailbox(username='test', osuser=User.objects.create_user()) + mailbox.set_password('test') + mailbox.save() + self.assertEqual( + self.mbadmin.get_fieldsets(Mock(), mailbox), + self.mbadmin.fieldsets) + + def test_get_form_without_object(self): + form = self.mbadmin.get_form(Mock) + self.assertEqual( + form.Meta.fields, + ['username', 'password1', 'password2'] + ) + + @override_settings( + CELERY_ALWAYS_EAGER=True, + CELERY_CACHE_BACKEND='memory', + BROKER_BACKEND='memory' + ) + def test_get_form_with_object(self): + mailbox = Mailbox(username='test', osuser=User.objects.create_user()) + mailbox.set_password('test') + mailbox.save() + form = self.mbadmin.get_form(Mock, mailbox) + self.assertEqual( + form.Meta.fields, + ['username', 'password', 'osuser', 'active'] + ) + + def test_admin_for_mailbox(self): + admin_url = reverse('admin:managemails_mailaddress_changelist') + self.assertIsNotNone(admin_url) + + +class TestMailAddressAdmin(TestCase): + def test_admin_for_mailaddress(self): + admin_url = reverse('admin:managemails_mailaddress_changelist') + self.assertIsNotNone(admin_url) From 32de6257d3eba6f8a3808a51d54361b62b355e57 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 18:29:41 +0200 Subject: [PATCH 30/48] add test for managemails.models.Mailbox.__str__ --- gnuviechadmin/managemails/tests/test_models.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/gnuviechadmin/managemails/tests/test_models.py b/gnuviechadmin/managemails/tests/test_models.py index a731ae0..4df1793 100644 --- a/gnuviechadmin/managemails/tests/test_models.py +++ b/gnuviechadmin/managemails/tests/test_models.py @@ -1,14 +1,22 @@ from django.test import TestCase +from django.test.utils import override_settings + from passlib.hash import sha512_crypt from domains.models import MailDomain from osusers.models import User + from managemails.models import ( MailAddress, Mailbox, ) +@override_settings( + CELERY_ALWAYS_EAGER=True, + CELERY_CACHE_BACKEND='memory', + BROKER_BACKEND='memory' +) class MailboxTest(TestCase): def test_set_password(self): user = User.objects.create_user() @@ -16,6 +24,12 @@ class MailboxTest(TestCase): mb.set_password('test') self.assertTrue(sha512_crypt.verify('test', mb.password)) + def test___str__(self): + user = User.objects.create_user() + mb = Mailbox.objects.create(username='test', osuser=user) + mb.set_password('test') + self.assertEqual(str(mb), 'test') + class MailAddressTest(TestCase): def test__str__(self): From 15ba5bd65344a56c6535016f12b46490b375a1ec Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 18:32:47 +0200 Subject: [PATCH 31/48] remove empty managemails.views --- gnuviechadmin/managemails/views.py | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 gnuviechadmin/managemails/views.py diff --git a/gnuviechadmin/managemails/views.py b/gnuviechadmin/managemails/views.py deleted file mode 100644 index 91ea44a..0000000 --- a/gnuviechadmin/managemails/views.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.shortcuts import render - -# Create your views here. From d47f964cbc9124da7f9823eb5e4cd45b7ad331d5 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 18:32:59 +0200 Subject: [PATCH 32/48] update changelog --- docs/changelog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index d13de71..4deb5ac 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,7 @@ Changelog ========= +* :feature:`-` full test suite for managemails app * :feature:`-` full test suite for domains app * :feature:`-` full test suite for osusers.models * :feature:`-` `Celery `_ integration for ldap From a663093433b3bc5ee7bcf3eb40d285704103d0a8 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 21:28:24 +0200 Subject: [PATCH 33/48] make sure to not execute LDAP task --- gnuviechadmin/osusers/tests/test_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnuviechadmin/osusers/tests/test_models.py b/gnuviechadmin/osusers/tests/test_models.py index 572cfcb..7fe2736 100644 --- a/gnuviechadmin/osusers/tests/test_models.py +++ b/gnuviechadmin/osusers/tests/test_models.py @@ -78,7 +78,7 @@ class AdditionalGroupTest(TestCaseWithCeleryTasks): @override_settings(OSUSER_MINGID=10000) -class GroupManagerTest(TestCase): +class GroupManagerTest(TestCaseWithCeleryTasks): def test_get_next_gid_first(self): self.assertEqual(Group.objects.get_next_gid(), 10000) From a95c5044bd51f6a8c386bd78b64974e83f4f9c60 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 19:21:17 +0200 Subject: [PATCH 34/48] remove unused gnuviechadmin.celery.debug_task --- gnuviechadmin/gnuviechadmin/celery.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/gnuviechadmin/gnuviechadmin/celery.py b/gnuviechadmin/gnuviechadmin/celery.py index 5a48fbf..b8be2e5 100644 --- a/gnuviechadmin/gnuviechadmin/celery.py +++ b/gnuviechadmin/gnuviechadmin/celery.py @@ -14,8 +14,3 @@ app = Celery('gnuviechadmin') app.config_from_object('django.conf:settings') app.autodiscover_tasks(lambda: settings.INSTALLED_APPS) - - -@app.task(bind=True) -def debug_task(self): - print('Request: {0!r}'.format(self.request)) From 773dbea0fe6f42aaed93b1abca064a8f0bb3a9da Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 22:01:04 +0200 Subject: [PATCH 35/48] implement osusers.tests.test_admin.TaskResultInlineTest --- gnuviechadmin/osusers/tests/test_admin.py | 31 +++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 gnuviechadmin/osusers/tests/test_admin.py diff --git a/gnuviechadmin/osusers/tests/test_admin.py b/gnuviechadmin/osusers/tests/test_admin.py new file mode 100644 index 0000000..fa1a524 --- /dev/null +++ b/gnuviechadmin/osusers/tests/test_admin.py @@ -0,0 +1,31 @@ +from django.test import TestCase +from django.contrib.admin import AdminSite + +from mock import patch, Mock + +from osusers.models import ( + User, +) +from osusers.admin import ( + UserTaskResultInline, +) + + +class TaskResultInlineTest(TestCase): + def setUp(self): + self.site = AdminSite() + super(TaskResultInlineTest, self).setUp() + + def test_get_queryset_calls_update_taskstatus(self): + with patch('osusers.admin.admin.TabularInline.get_queryset') as mock: + entrymock = Mock(name='entry') + mock.return_value = [entrymock] + requestmock = Mock(name='request') + UserTaskResultInline(User, self.site).get_queryset(requestmock) + entrymock.assert_calledwith() + + def test_has_add_permissions_returns_false(self): + self.assertFalse( + UserTaskResultInline(User, self.site).has_add_permission( + self, Mock(name='request')) + ) From 5cc86b47902380319204f513d0346bfcfb5e6ea4 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 22:18:16 +0200 Subject: [PATCH 36/48] change osusers.admin.UserCreationForm.save_m2m - replace pass with a doc string to explain why this method does nothing --- gnuviechadmin/osusers/admin.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gnuviechadmin/osusers/admin.py b/gnuviechadmin/osusers/admin.py index 3a23b82..fb21576 100644 --- a/gnuviechadmin/osusers/admin.py +++ b/gnuviechadmin/osusers/admin.py @@ -84,7 +84,10 @@ class UserCreationForm(forms.ModelForm): return user def save_m2m(self): - pass + """ + No additional groups are created when this form is saved, so this + method just does nothing. + """ class UserAdmin(admin.ModelAdmin): From 3db6b99a002737ad18d45ae84ed927d577c15a24 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 22:20:28 +0200 Subject: [PATCH 37/48] add osuser.tests.test_admin.UserCreationFormTest --- gnuviechadmin/osusers/tests/test_admin.py | 37 ++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/gnuviechadmin/osusers/tests/test_admin.py b/gnuviechadmin/osusers/tests/test_admin.py index fa1a524..41a3867 100644 --- a/gnuviechadmin/osusers/tests/test_admin.py +++ b/gnuviechadmin/osusers/tests/test_admin.py @@ -1,5 +1,7 @@ -from django.test import TestCase +from django import forms from django.contrib.admin import AdminSite +from django.test import TestCase +from django.test.utils import override_settings from mock import patch, Mock @@ -7,6 +9,8 @@ from osusers.models import ( User, ) from osusers.admin import ( + PASSWORD_MISMATCH_ERROR, + UserCreationForm, UserTaskResultInline, ) @@ -29,3 +33,34 @@ class TaskResultInlineTest(TestCase): UserTaskResultInline(User, self.site).has_add_permission( self, Mock(name='request')) ) + + +class UserCreationFormTest(TestCase): + def test_clean_password2_same(self): + form = UserCreationForm() + form.cleaned_data = {'password1': 'secret', 'password2': 'secret'} + self.assertEqual(form.clean_password2(), 'secret') + + def test_clean_password2_empty(self): + form = UserCreationForm() + form.cleaned_data = {} + self.assertIsNone(form.clean_password2()) + + def test_clean_password2_mismatch(self): + form = UserCreationForm() + form.cleaned_data = {'password1': 'secretx', 'password2': 'secrety'} + with self.assertRaises(forms.ValidationError) as cm: + form.clean_password2() + self.assertEqual(cm.exception.message, PASSWORD_MISMATCH_ERROR) + + @override_settings( + CELERY_ALWAYS_EAGER=True, + CELERY_CACHE_BACKEND='memory', + BROKER_BACKEND='memory' + ) + def test_save_commit(self): + form = UserCreationForm() + form.cleaned_data = {'password1': 'secret', 'password2': 'secret'} + user = form.save() + self.assertIsNotNone(user) + self.assertEqual(User.objects.get(pk=user.uid), user) From 09f72fc0918507190d108e6aa85bbb20db034fd8 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 22:37:43 +0200 Subject: [PATCH 38/48] add osusers.tests.test_admin.UserAdminTest --- gnuviechadmin/osusers/tests/test_admin.py | 43 +++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/gnuviechadmin/osusers/tests/test_admin.py b/gnuviechadmin/osusers/tests/test_admin.py index 41a3867..ac8f10d 100644 --- a/gnuviechadmin/osusers/tests/test_admin.py +++ b/gnuviechadmin/osusers/tests/test_admin.py @@ -10,6 +10,7 @@ from osusers.models import ( ) from osusers.admin import ( PASSWORD_MISMATCH_ERROR, + UserAdmin, UserCreationForm, UserTaskResultInline, ) @@ -64,3 +65,45 @@ class UserCreationFormTest(TestCase): user = form.save() self.assertIsNotNone(user) self.assertEqual(User.objects.get(pk=user.uid), user) + + +class UserAdminTest(TestCase): + def setUp(self): + site = AdminSite() + self.uadmin = UserAdmin(User, site) + super(UserAdminTest, self).setUp() + + def test_get_form_without_object(self): + form = self.uadmin.get_form(Mock(name='request')) + self.assertEqual( + form.Meta.fields, + ['password1', 'password2'] + ) + + @override_settings( + CELERY_ALWAYS_EAGER=True, + CELERY_CACHE_BACKEND='memory', + BROKER_BACKEND='memory' + ) + def test_get_form_with_object(self): + user = User.objects.create_user() + form = self.uadmin.get_form(Mock(name='request'), user) + self.assertEqual( + form.Meta.fields, + ['username', 'group', 'gecos', 'homedir', 'shell', 'uid'] + ) + + def test_get_inline_instances_without_object(self): + inlines = self.uadmin.get_inline_instances(Mock(name='request')) + self.assertEqual(inlines, []) + + @override_settings( + CELERY_ALWAYS_EAGER=True, + CELERY_CACHE_BACKEND='memory', + BROKER_BACKEND='memory' + ) + def test_get_inline_instances_with_object(self): + user = User.objects.create_user() + inlines = self.uadmin.get_inline_instances( + Mock(name='request'), user) + self.assertEqual(len(inlines), len(UserAdmin.inlines)) From b9d39f31a4c42a4334682af0f49b14b28632a56a Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 22:41:42 +0200 Subject: [PATCH 39/48] rename test class names from Test* to *Test --- gnuviechadmin/managemails/tests/test_admin.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/gnuviechadmin/managemails/tests/test_admin.py b/gnuviechadmin/managemails/tests/test_admin.py index fbe00b7..58e99b7 100644 --- a/gnuviechadmin/managemails/tests/test_admin.py +++ b/gnuviechadmin/managemails/tests/test_admin.py @@ -25,7 +25,7 @@ from managemails.models import ( ) -class TestReadOnlyPasswordHashWidget(TestCase): +class ReadOnlyPasswordHashWidgetTest(TestCase): def test_render(self): widget = ReadOnlyPasswordHashWidget() rendered = widget.render('password', 'secret', {'class': 'test'}) @@ -38,7 +38,7 @@ class TestReadOnlyPasswordHashWidget(TestCase): )) -class TestReadOnlyPasswordHashField(TestCase): +class ReadOnlyPasswordHashFieldTest(TestCase): def test___init__(self): field = ReadOnlyPasswordHashField() self.assertFalse(field.required) @@ -52,7 +52,7 @@ class TestReadOnlyPasswordHashField(TestCase): self.assertFalse(field._has_changed('new', 'old')) -class TestMailboxCreationForm(TestCase): +class MailboxCreationFormTest(TestCase): def test_clean_password2_same(self): form = MailboxCreationForm() form.cleaned_data = {'password1': 'secret', 'password2': 'secret'} @@ -105,7 +105,7 @@ class TestMailboxCreationForm(TestCase): len(Mailbox.objects.filter(osuser=user)), 0) -class TestMailboxChangeForm(TestCase): +class MailboxChangeFormTest(TestCase): @override_settings( CELERY_ALWAYS_EAGER=True, CELERY_CACHE_BACKEND='memory', @@ -119,7 +119,7 @@ class TestMailboxChangeForm(TestCase): self.assertEqual(form.clean_password(), mailbox.password) -class TestActivationChangeMixin(TestCase): +class ActivationChangeMixinTest(TestCase): def test_activate(self): querysetmock = Mock() activationchange = ActivationChangeMixin() @@ -133,7 +133,7 @@ class TestActivationChangeMixin(TestCase): querysetmock.update.called_with(active=False) -class TestMailBoxAdmin(TestCase): +class MailBoxAdminTest(TestCase): def setUp(self): site = AdminSite() self.mbadmin = MailboxAdmin(Mailbox, site) @@ -183,7 +183,7 @@ class TestMailBoxAdmin(TestCase): self.assertIsNotNone(admin_url) -class TestMailAddressAdmin(TestCase): +class MailAddressAdminTest(TestCase): def test_admin_for_mailaddress(self): admin_url = reverse('admin:managemails_mailaddress_changelist') self.assertIsNotNone(admin_url) From 338b575983f6e9a3d62c73fb4f8adf9caf37d85b Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 22:53:29 +0200 Subject: [PATCH 40/48] add type check --- gnuviechadmin/osusers/tests/test_admin.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gnuviechadmin/osusers/tests/test_admin.py b/gnuviechadmin/osusers/tests/test_admin.py index ac8f10d..87d0c10 100644 --- a/gnuviechadmin/osusers/tests/test_admin.py +++ b/gnuviechadmin/osusers/tests/test_admin.py @@ -107,3 +107,5 @@ class UserAdminTest(TestCase): inlines = self.uadmin.get_inline_instances( Mock(name='request'), user) self.assertEqual(len(inlines), len(UserAdmin.inlines)) + for index in range(len(inlines)): + self.assertIsInstance(inlines[index], UserAdmin.inlines[index]) From 266794d4475ae1651eb2edaa35a74bc962f376e2 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 22:53:38 +0200 Subject: [PATCH 41/48] add osusers.tests.test_admin.GroupAdminTest --- gnuviechadmin/osusers/tests/test_admin.py | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/gnuviechadmin/osusers/tests/test_admin.py b/gnuviechadmin/osusers/tests/test_admin.py index 87d0c10..1b80d13 100644 --- a/gnuviechadmin/osusers/tests/test_admin.py +++ b/gnuviechadmin/osusers/tests/test_admin.py @@ -6,9 +6,11 @@ from django.test.utils import override_settings from mock import patch, Mock from osusers.models import ( + Group, User, ) from osusers.admin import ( + GroupAdmin, PASSWORD_MISMATCH_ERROR, UserAdmin, UserCreationForm, @@ -109,3 +111,27 @@ class UserAdminTest(TestCase): self.assertEqual(len(inlines), len(UserAdmin.inlines)) for index in range(len(inlines)): self.assertIsInstance(inlines[index], UserAdmin.inlines[index]) + + +class GroupAdminTest(TestCase): + def setUp(self): + site = AdminSite() + self.gadmin = GroupAdmin(Group, site) + super(GroupAdminTest, self).setUp() + + def test_get_inline_instances_without_object(self): + inlines = self.gadmin.get_inline_instances(Mock(name='request')) + self.assertEqual(inlines, []) + + @override_settings( + CELERY_ALWAYS_EAGER=True, + CELERY_CACHE_BACKEND='memory', + BROKER_BACKEND='memory' + ) + def test_get_inline_instances_with_object(self): + group = Group.objects.create(gid=1000, groupname='test') + inlines = self.gadmin.get_inline_instances( + Mock(name='request'), group) + self.assertEqual(len(inlines), len(GroupAdmin.inlines)) + for index in range(len(inlines)): + self.assertIsInstance(inlines[index], GroupAdmin.inlines[index]) From 6b36cc95ff35c548f5a210575e386336d0e25fc7 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 23:05:28 +0200 Subject: [PATCH 42/48] really test that update_taskstatus is called --- gnuviechadmin/osusers/tests/test_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnuviechadmin/osusers/tests/test_admin.py b/gnuviechadmin/osusers/tests/test_admin.py index 1b80d13..fe2faaa 100644 --- a/gnuviechadmin/osusers/tests/test_admin.py +++ b/gnuviechadmin/osusers/tests/test_admin.py @@ -29,7 +29,7 @@ class TaskResultInlineTest(TestCase): mock.return_value = [entrymock] requestmock = Mock(name='request') UserTaskResultInline(User, self.site).get_queryset(requestmock) - entrymock.assert_calledwith() + entrymock.update_taskstatus.assert_calledwith() def test_has_add_permissions_returns_false(self): self.assertFalse( From 0a48619f3c6ea0db9921f4e1e4b8192b36b14349 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 23:05:58 +0200 Subject: [PATCH 43/48] implement osusers.tests.test_admin.DeleteTaskResultAdminTest --- gnuviechadmin/osusers/tests/test_admin.py | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/gnuviechadmin/osusers/tests/test_admin.py b/gnuviechadmin/osusers/tests/test_admin.py index fe2faaa..12091ba 100644 --- a/gnuviechadmin/osusers/tests/test_admin.py +++ b/gnuviechadmin/osusers/tests/test_admin.py @@ -6,10 +6,12 @@ from django.test.utils import override_settings from mock import patch, Mock from osusers.models import ( + DeleteTaskResult, Group, User, ) from osusers.admin import ( + DeleteTaskResultAdmin, GroupAdmin, PASSWORD_MISMATCH_ERROR, UserAdmin, @@ -135,3 +137,27 @@ class GroupAdminTest(TestCase): self.assertEqual(len(inlines), len(GroupAdmin.inlines)) for index in range(len(inlines)): self.assertIsInstance(inlines[index], GroupAdmin.inlines[index]) + + +class DeleteTaskResultAdminTest(TestCase): + def setUp(self): + site = AdminSite() + self.dtradmin = DeleteTaskResultAdmin(DeleteTaskResult, site) + super(DeleteTaskResultAdminTest, self).setUp() + + def test_has_add_permission_returns_false_without_object(self): + self.assertFalse( + self.dtradmin.has_add_permission(Mock(name='request'))) + + def test_has_add_permission_returns_false_with_object(self): + self.assertFalse( + self.dtradmin.has_add_permission(Mock(name='request'), + Mock(name='test'))) + + def test_get_queryset_calls_update_taskstatus(self): + with patch('osusers.admin.admin.ModelAdmin.get_queryset') as mock: + entrymock = Mock(name='entry') + mock.return_value = [entrymock] + requestmock = Mock(name='request') + self.dtradmin.get_queryset(requestmock) + entrymock.update_taskstatus.assert_calledwith() From fa5f296c0f25a2eac64cf53675380481ad53abe5 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 23:10:26 +0200 Subject: [PATCH 44/48] add test for UserCreationForm.save_m2m --- gnuviechadmin/osusers/tests/test_admin.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gnuviechadmin/osusers/tests/test_admin.py b/gnuviechadmin/osusers/tests/test_admin.py index 12091ba..cf75a5a 100644 --- a/gnuviechadmin/osusers/tests/test_admin.py +++ b/gnuviechadmin/osusers/tests/test_admin.py @@ -70,6 +70,10 @@ class UserCreationFormTest(TestCase): self.assertIsNotNone(user) self.assertEqual(User.objects.get(pk=user.uid), user) + def test_save_m2m_returns_none(self): + form = UserCreationForm() + self.assertIsNone(form.save_m2m()) + class UserAdminTest(TestCase): def setUp(self): From f01c3dbabbdd466f3d160d9a295e93160e24ebaa Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 23:14:16 +0200 Subject: [PATCH 45/48] remove empty osusers/views.py --- gnuviechadmin/osusers/views.py | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 gnuviechadmin/osusers/views.py diff --git a/gnuviechadmin/osusers/views.py b/gnuviechadmin/osusers/views.py deleted file mode 100644 index 91ea44a..0000000 --- a/gnuviechadmin/osusers/views.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.shortcuts import render - -# Create your views here. From 2204f7df4390e344e4761baaa7d1f83b6f05d9ad Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 23:22:13 +0200 Subject: [PATCH 46/48] add osusers.tests.test_tasks.LdapRouterTest --- gnuviechadmin/osusers/tests/test_tasks.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 gnuviechadmin/osusers/tests/test_tasks.py diff --git a/gnuviechadmin/osusers/tests/test_tasks.py b/gnuviechadmin/osusers/tests/test_tasks.py new file mode 100644 index 0000000..e3b409f --- /dev/null +++ b/gnuviechadmin/osusers/tests/test_tasks.py @@ -0,0 +1,22 @@ +from django.test import TestCase + +from osusers.tasks import LdapRouter + + +class LdapRouterTest(TestCase): + def setUp(self): + self.router = LdapRouter() + super(LdapRouterTest, self).setUp() + + def test_ldap_tasks_are_routed_to_ldap_queue(self): + route = self.router.route_for_task( + 'some_ldap_task') + self.assertEqual( + route, + {'exchange': 'ldap', + 'exchange_type': 'direct', + 'queue': 'ldap'}) + + def test_non_ldap_tasks_are_routed_to_default(self): + self.assertIsNone( + self.router.route_for_task('other')) From d8c0984b05deb9a24a8fe4ae2c31d4a32f5bd3fc Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 23:24:38 +0200 Subject: [PATCH 47/48] update changelog --- docs/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 4deb5ac..24ee23a 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,9 +1,9 @@ Changelog ========= +* :feature:`-` full test suite for osusers * :feature:`-` full test suite for managemails app * :feature:`-` full test suite for domains app -* :feature:`-` full test suite for osusers.models * :feature:`-` `Celery `_ integration for ldap synchronization From 4be6b9ec3c44924c73860257f887fbf950f8f7d1 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 1 Jun 2014 23:26:44 +0200 Subject: [PATCH 48/48] bump version number, add release to changelog --- 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 24ee23a..099a067 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,7 @@ Changelog ========= +* :release:`0.2.0 <2014-06-01>` * :feature:`-` full test suite for osusers * :feature:`-` full test suite for managemails app * :feature:`-` full test suite for domains app diff --git a/docs/conf.py b/docs/conf.py index 228b845..3a6ad3a 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.1' +version = '0.2.0' # The full version, including alpha/beta/rc tags. -release = '0.1' +release = '0.2.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages.