From 81f1faee6c0e5f207d141780e9b758f576d4b8e5 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 25 May 2014 23:33:37 +0200 Subject: [PATCH 1/6] 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 2/6] 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 3/6] 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 4/6] 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 5/6] 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 6/6] 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