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)) diff --git a/gnuviechadmin/gnuviechadmin/settings/base.py b/gnuviechadmin/gnuviechadmin/settings/base.py index 0a1b858..6126077 100644 --- a/gnuviechadmin/gnuviechadmin/settings/base.py +++ b/gnuviechadmin/gnuviechadmin/settings/base.py @@ -279,6 +279,17 @@ 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 +CELERY_ROUTES = ( + 'osusers.tasks.LdapRouter', +) +########## 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')) 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..c513574 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,15 @@ from model_utils.models import TimeStampedModel 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, +) + class GroupManager(models.Manager): @@ -42,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): @@ -59,7 +77,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 +85,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 +93,15 @@ 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) - shadow = Shadow.objects.create_shadow(user=user, password=password) - user.save() - shadow.save() - transaction.commit() - if autocommit: - transaction.set_autocommit(True) + Shadow.objects.create_shadow(user=user, password=password) + user.set_password(password) + if commit: + user.save() return user @@ -111,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): @@ -185,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 new file mode 100644 index 0000000..5eff249 --- /dev/null +++ b/gnuviechadmin/osusers/tasks.py @@ -0,0 +1,43 @@ +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 + + +@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 diff --git a/requirements/base.txt b/requirements/base.txt index 2cedcab..adc811d 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -6,3 +6,7 @@ logutils==0.3.3 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