document osusers code
This commit is contained in:
parent
18e47d73b4
commit
0df67e7154
5 changed files with 430 additions and 27 deletions
|
@ -0,0 +1,5 @@
|
||||||
|
"""
|
||||||
|
This app is for managing operating system users and groups.
|
||||||
|
|
||||||
|
"""
|
||||||
|
default_app_config = 'osusers.apps.OsusersAppConfig'
|
|
@ -1,3 +1,7 @@
|
||||||
|
"""
|
||||||
|
This module contains the Django admin classes of the :py:mod:`osusers` app.
|
||||||
|
|
||||||
|
"""
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
@ -10,13 +14,24 @@ from .models import (
|
||||||
)
|
)
|
||||||
|
|
||||||
PASSWORD_MISMATCH_ERROR = _("Passwords don't match")
|
PASSWORD_MISMATCH_ERROR = _("Passwords don't match")
|
||||||
|
"""
|
||||||
|
Error message for non matching passwords.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class AdditionalGroupInline(admin.TabularInline):
|
class AdditionalGroupInline(admin.TabularInline):
|
||||||
|
"""
|
||||||
|
Inline for :py:class:`osusers.models.AdditionalGroup` instances.
|
||||||
|
|
||||||
|
"""
|
||||||
model = AdditionalGroup
|
model = AdditionalGroup
|
||||||
|
|
||||||
|
|
||||||
class ShadowInline(admin.TabularInline):
|
class ShadowInline(admin.TabularInline):
|
||||||
|
"""
|
||||||
|
Inline for :py:class:`osusers.models.ShadowInline` instances.
|
||||||
|
|
||||||
|
"""
|
||||||
model = Shadow
|
model = Shadow
|
||||||
readonly_fields = ['passwd']
|
readonly_fields = ['passwd']
|
||||||
can_delete = False
|
can_delete = False
|
||||||
|
@ -24,7 +39,8 @@ class ShadowInline(admin.TabularInline):
|
||||||
|
|
||||||
class UserCreationForm(forms.ModelForm):
|
class UserCreationForm(forms.ModelForm):
|
||||||
"""
|
"""
|
||||||
A form for creating system users.
|
A form for creating :py:class:`operating system users
|
||||||
|
<osusers.models.User>`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
password1 = forms.CharField(
|
password1 = forms.CharField(
|
||||||
|
@ -44,6 +60,9 @@ class UserCreationForm(forms.ModelForm):
|
||||||
"""
|
"""
|
||||||
Check that the two password entries match.
|
Check that the two password entries match.
|
||||||
|
|
||||||
|
:return: the validated password
|
||||||
|
:rtype: str or None
|
||||||
|
|
||||||
"""
|
"""
|
||||||
password1 = self.cleaned_data.get('password1')
|
password1 = self.cleaned_data.get('password1')
|
||||||
password2 = self.cleaned_data.get('password2')
|
password2 = self.cleaned_data.get('password2')
|
||||||
|
@ -55,6 +74,10 @@ class UserCreationForm(forms.ModelForm):
|
||||||
"""
|
"""
|
||||||
Save the provided password in hashed format.
|
Save the provided password in hashed format.
|
||||||
|
|
||||||
|
:param boolean commit: whether to save the created user
|
||||||
|
:return: user instance
|
||||||
|
:rtype: :py:class:`osusers.models.User`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
user = User.objects.create_user(
|
user = User.objects.create_user(
|
||||||
customer=self.cleaned_data['customer'],
|
customer=self.cleaned_data['customer'],
|
||||||
|
@ -65,10 +88,16 @@ class UserCreationForm(forms.ModelForm):
|
||||||
"""
|
"""
|
||||||
No additional groups are created when this form is saved, so this
|
No additional groups are created when this form is saved, so this
|
||||||
method just does nothing.
|
method just does nothing.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class UserAdmin(admin.ModelAdmin):
|
class UserAdmin(admin.ModelAdmin):
|
||||||
|
"""
|
||||||
|
Admin class for working with :py:class:`operating system users
|
||||||
|
<osusers.models.User>`.
|
||||||
|
|
||||||
|
"""
|
||||||
actions = ['perform_delete_selected']
|
actions = ['perform_delete_selected']
|
||||||
add_form = UserCreationForm
|
add_form = UserCreationForm
|
||||||
inlines = [AdditionalGroupInline, ShadowInline]
|
inlines = [AdditionalGroupInline, ShadowInline]
|
||||||
|
@ -83,6 +112,13 @@ class UserAdmin(admin.ModelAdmin):
|
||||||
"""
|
"""
|
||||||
Use special form during user creation.
|
Use special form during user creation.
|
||||||
|
|
||||||
|
:param request: the current HTTP request
|
||||||
|
:param obj: either a :py:class:`User <osusers.models.User>` instance or
|
||||||
|
None for a new user
|
||||||
|
:param kwargs: keyword arguments to be passed to
|
||||||
|
:py:meth:`django.contrib.admin.ModelAdmin.get_form`
|
||||||
|
:return: form instance
|
||||||
|
|
||||||
"""
|
"""
|
||||||
defaults = {}
|
defaults = {}
|
||||||
if obj is None:
|
if obj is None:
|
||||||
|
@ -94,16 +130,47 @@ class UserAdmin(admin.ModelAdmin):
|
||||||
return super(UserAdmin, self).get_form(request, obj, **defaults)
|
return super(UserAdmin, self).get_form(request, obj, **defaults)
|
||||||
|
|
||||||
def get_readonly_fields(self, request, obj=None):
|
def get_readonly_fields(self, request, obj=None):
|
||||||
|
"""
|
||||||
|
Make sure that uid is not editable for existing users.
|
||||||
|
|
||||||
|
:param request: the current HTTP request
|
||||||
|
:param obj: either a :py:class:`User <osusers.models.User>` instance or
|
||||||
|
None for a new user
|
||||||
|
:return: a list of fields
|
||||||
|
:rtype: list
|
||||||
|
|
||||||
|
"""
|
||||||
if obj:
|
if obj:
|
||||||
return ['uid']
|
return ['uid']
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def perform_delete_selected(self, request, queryset):
|
def perform_delete_selected(self, request, queryset):
|
||||||
|
"""
|
||||||
|
Action to delete a list of selected users.
|
||||||
|
|
||||||
|
This action calls the delete method of each selected user in contrast
|
||||||
|
to the default `delete_selected`.
|
||||||
|
|
||||||
|
:param request: the current HTTP request
|
||||||
|
:param queryset: Django ORM queryset representing the selected users
|
||||||
|
|
||||||
|
"""
|
||||||
for user in queryset.all():
|
for user in queryset.all():
|
||||||
user.delete()
|
user.delete()
|
||||||
perform_delete_selected.short_description = _('Delete selected users')
|
perform_delete_selected.short_description = _('Delete selected users')
|
||||||
|
|
||||||
def get_actions(self, request):
|
def get_actions(self, request):
|
||||||
|
"""
|
||||||
|
Get the available actions for users.
|
||||||
|
|
||||||
|
This overrides the default behavior to remove the default
|
||||||
|
`delete_selected` action.
|
||||||
|
|
||||||
|
:param request: the current HTTP request
|
||||||
|
:return: list of actions
|
||||||
|
:rtype: list
|
||||||
|
|
||||||
|
"""
|
||||||
actions = super(UserAdmin, self).get_actions(request)
|
actions = super(UserAdmin, self).get_actions(request)
|
||||||
if 'delete_selected' in actions:
|
if 'delete_selected' in actions:
|
||||||
del actions['delete_selected']
|
del actions['delete_selected']
|
||||||
|
@ -111,19 +178,40 @@ class UserAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
|
|
||||||
class GroupAdmin(admin.ModelAdmin):
|
class GroupAdmin(admin.ModelAdmin):
|
||||||
|
"""
|
||||||
|
Admin class for workint with :py:class:`operating system groups
|
||||||
|
<osusers.models.Group>`.
|
||||||
|
|
||||||
|
"""
|
||||||
actions = ['perform_delete_selected']
|
actions = ['perform_delete_selected']
|
||||||
|
|
||||||
def get_inline_instances(self, request, obj=None):
|
|
||||||
if obj is None:
|
|
||||||
return []
|
|
||||||
return super(GroupAdmin, self).get_inline_instances(request, obj)
|
|
||||||
|
|
||||||
def perform_delete_selected(self, request, queryset):
|
def perform_delete_selected(self, request, queryset):
|
||||||
|
"""
|
||||||
|
Action to delete a list of selected groups.
|
||||||
|
|
||||||
|
This action calls the delete method of each selected group in contrast
|
||||||
|
to the default `delete_selected`.
|
||||||
|
|
||||||
|
:param request: the current HTTP request
|
||||||
|
:param queryset: Django ORM queryset representing the selected groups
|
||||||
|
|
||||||
|
"""
|
||||||
for group in queryset.all():
|
for group in queryset.all():
|
||||||
group.delete()
|
group.delete()
|
||||||
perform_delete_selected.short_description = _('Delete selected groups')
|
perform_delete_selected.short_description = _('Delete selected groups')
|
||||||
|
|
||||||
def get_actions(self, request):
|
def get_actions(self, request):
|
||||||
|
"""
|
||||||
|
Get the available actions for groups.
|
||||||
|
|
||||||
|
This overrides the default behavior to remove the default
|
||||||
|
`delete_selected` action.
|
||||||
|
|
||||||
|
:param request: the current HTTP request
|
||||||
|
:return: list of actions
|
||||||
|
:rtype: list
|
||||||
|
|
||||||
|
"""
|
||||||
actions = super(GroupAdmin, self).get_actions(request)
|
actions = super(GroupAdmin, self).get_actions(request)
|
||||||
if 'delete_selected' in actions:
|
if 'delete_selected' in actions:
|
||||||
del actions['delete_selected']
|
del actions['delete_selected']
|
||||||
|
|
17
gnuviechadmin/osusers/apps.py
Normal file
17
gnuviechadmin/osusers/apps.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
"""
|
||||||
|
This module contains the :py:class:`django.apps.AppConfig` instance for the
|
||||||
|
:py:module:`osusers` app.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
from django.apps import AppConfig
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class OsusersAppConfig(AppConfig):
|
||||||
|
"""
|
||||||
|
AppConfig for the :py:mod:`osusers` app.
|
||||||
|
|
||||||
|
"""
|
||||||
|
name = 'osusers'
|
||||||
|
verbose_name = _('Operating System Users and Groups')
|
|
@ -1,3 +1,7 @@
|
||||||
|
"""
|
||||||
|
This module defines the database models of operating system users.
|
||||||
|
|
||||||
|
"""
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
@ -30,7 +34,7 @@ from .tasks import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
CANNOT_USE_PRIMARY_GROUP_AS_ADDITIONAL = _(
|
CANNOT_USE_PRIMARY_GROUP_AS_ADDITIONAL = _(
|
||||||
|
@ -38,8 +42,19 @@ CANNOT_USE_PRIMARY_GROUP_AS_ADDITIONAL = _(
|
||||||
|
|
||||||
|
|
||||||
class GroupManager(models.Manager):
|
class GroupManager(models.Manager):
|
||||||
|
"""
|
||||||
|
Manager class for :py:class:`osusers.models.Group`.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
def get_next_gid(self):
|
def get_next_gid(self):
|
||||||
|
"""
|
||||||
|
Get the next available group id.
|
||||||
|
|
||||||
|
:returns: group id
|
||||||
|
:rtype: int
|
||||||
|
|
||||||
|
"""
|
||||||
q = self.aggregate(models.Max('gid'))
|
q = self.aggregate(models.Max('gid'))
|
||||||
if q['gid__max'] is None:
|
if q['gid__max'] is None:
|
||||||
return settings.OSUSER_MINGID
|
return settings.OSUSER_MINGID
|
||||||
|
@ -48,6 +63,10 @@ class GroupManager(models.Manager):
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Group(TimeStampedModel, models.Model):
|
class Group(TimeStampedModel, models.Model):
|
||||||
|
"""
|
||||||
|
This entity class corresponds to an operating system group.
|
||||||
|
|
||||||
|
"""
|
||||||
groupname = models.CharField(
|
groupname = models.CharField(
|
||||||
_('Group name'), max_length=16, unique=True)
|
_('Group name'), max_length=16, unique=True)
|
||||||
gid = models.PositiveSmallIntegerField(
|
gid = models.PositiveSmallIntegerField(
|
||||||
|
@ -67,34 +86,73 @@ class Group(TimeStampedModel, models.Model):
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Save the group to the database and synchronizes group information to
|
||||||
|
LDAP.
|
||||||
|
|
||||||
|
:param args: positional arguments to be passed on to
|
||||||
|
:py:meth:`django.db.Model.save`
|
||||||
|
:param kwargs: keyword arguments to be passed on to
|
||||||
|
:py:meth:`django.db.Model.save`
|
||||||
|
:return: self
|
||||||
|
:rtype: :py:class:`osusers.models.Group`
|
||||||
|
|
||||||
|
"""
|
||||||
super(Group, self).save(*args, **kwargs)
|
super(Group, self).save(*args, **kwargs)
|
||||||
dn = create_ldap_group.delay(
|
dn = create_ldap_group.delay(
|
||||||
self.groupname, self.gid, self.descr).get()
|
self.groupname, self.gid, self.descr).get()
|
||||||
logger.info("created LDAP group with dn %s", dn)
|
_LOGGER.info("created LDAP group with dn %s", dn)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def delete(self, *args, **kwargs):
|
def delete(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Delete the group from LDAP and the database.
|
||||||
|
|
||||||
|
:param args: positional arguments to be passed on to
|
||||||
|
:py:meth:`django.db.Model.delete`
|
||||||
|
:param kwargs: keyword arguments to be passed on to
|
||||||
|
:py:meth:`django.db.Model.delete`
|
||||||
|
|
||||||
|
"""
|
||||||
delete_ldap_group_if_empty.delay(self.groupname).get()
|
delete_ldap_group_if_empty.delay(self.groupname).get()
|
||||||
super(Group, self).delete(*args, **kwargs)
|
super(Group, self).delete(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class UserManager(models.Manager):
|
class UserManager(models.Manager):
|
||||||
|
"""
|
||||||
|
Manager class for :py:class:`osusers.models.User`.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
def get_next_uid(self):
|
def get_next_uid(self):
|
||||||
|
"""
|
||||||
|
Get the next available user id.
|
||||||
|
|
||||||
|
:return: user id
|
||||||
|
:rtype: int
|
||||||
|
|
||||||
|
"""
|
||||||
q = self.aggregate(models.Max('uid'))
|
q = self.aggregate(models.Max('uid'))
|
||||||
if q['uid__max'] is None:
|
if q['uid__max'] is None:
|
||||||
return settings.OSUSER_MINUID
|
return settings.OSUSER_MINUID
|
||||||
return max(settings.OSUSER_MINUID, q['uid__max'] + 1)
|
return max(settings.OSUSER_MINUID, q['uid__max'] + 1)
|
||||||
|
|
||||||
def get_next_username(self):
|
def get_next_username(self):
|
||||||
|
"""
|
||||||
|
Get the next available user name.
|
||||||
|
|
||||||
|
:return: user name
|
||||||
|
:rtype: str
|
||||||
|
|
||||||
|
"""
|
||||||
count = 1
|
count = 1
|
||||||
usernameformat = "{0}{1:02d}"
|
usernameformat = "{0}{1:02d}"
|
||||||
nextuser = usernameformat.format(settings.OSUSER_USERNAME_PREFIX,
|
nextuser = usernameformat.format(settings.OSUSER_USERNAME_PREFIX,
|
||||||
count)
|
count)
|
||||||
for user in self.values('username').filter(
|
for user in self.values('username').filter(
|
||||||
username__startswith=settings.OSUSER_USERNAME_PREFIX).order_by(
|
username__startswith=settings.OSUSER_USERNAME_PREFIX
|
||||||
'username'):
|
).order_by('username'):
|
||||||
if user['username'] == nextuser:
|
if user['username'] == nextuser:
|
||||||
count += 1
|
count += 1
|
||||||
nextuser = usernameformat.format(
|
nextuser = usernameformat.format(
|
||||||
|
@ -107,6 +165,23 @@ class UserManager(models.Manager):
|
||||||
def create_user(
|
def create_user(
|
||||||
self, customer, username=None, password=None, commit=False
|
self, customer, username=None, password=None, commit=False
|
||||||
):
|
):
|
||||||
|
"""
|
||||||
|
Create a new user with a primary group named the same as the user and
|
||||||
|
an initial password.
|
||||||
|
|
||||||
|
If username is None the result of :py:meth:`get_next_username` is used.
|
||||||
|
If password is None a new password will be generated using passlib's
|
||||||
|
:py:func:`generate_password`.
|
||||||
|
|
||||||
|
:param customer: Django User instance this user is associated to
|
||||||
|
:param str username: the username or None
|
||||||
|
:param str password: the password or None
|
||||||
|
:param boolean commit: whether to commit the user data to the database
|
||||||
|
or not
|
||||||
|
:return: new user
|
||||||
|
:rtype: :py:class:`osusers.models.User`
|
||||||
|
|
||||||
|
"""
|
||||||
uid = self.get_next_uid()
|
uid = self.get_next_uid()
|
||||||
gid = Group.objects.get_next_gid()
|
gid = Group.objects.get_next_gid()
|
||||||
if username is None:
|
if username is None:
|
||||||
|
@ -126,6 +201,10 @@ class UserManager(models.Manager):
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class User(TimeStampedModel, models.Model):
|
class User(TimeStampedModel, models.Model):
|
||||||
|
"""
|
||||||
|
This entity class corresponds to an operating system user.
|
||||||
|
|
||||||
|
"""
|
||||||
username = models.CharField(
|
username = models.CharField(
|
||||||
_('User name'), max_length=64, unique=True)
|
_('User name'), max_length=64, unique=True)
|
||||||
uid = models.PositiveSmallIntegerField(
|
uid = models.PositiveSmallIntegerField(
|
||||||
|
@ -147,6 +226,15 @@ class User(TimeStampedModel, models.Model):
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def set_password(self, password):
|
def set_password(self, password):
|
||||||
|
"""
|
||||||
|
Set the password of the user.
|
||||||
|
|
||||||
|
The password is set to the user's
|
||||||
|
:py:class:`Shadow <osusers.models.Shadow>` instance and to LDAP.
|
||||||
|
|
||||||
|
:param str password: the new password
|
||||||
|
|
||||||
|
"""
|
||||||
if hasattr(self, 'shadow'):
|
if hasattr(self, 'shadow'):
|
||||||
self.shadow.set_password(password)
|
self.shadow.set_password(password)
|
||||||
else:
|
else:
|
||||||
|
@ -161,12 +249,24 @@ class User(TimeStampedModel, models.Model):
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Save the user to the database, create user directories and synchronize
|
||||||
|
user information to LDAP.
|
||||||
|
|
||||||
|
:param args: positional arguments to be passed on to
|
||||||
|
:py:meth:`django.db.Model.save`
|
||||||
|
:param kwargs: keyword arguments to be passed on to
|
||||||
|
:py:meth:`django.db.Model.save`
|
||||||
|
:return: self
|
||||||
|
:rtype: :py:class:`osusers.models.User`
|
||||||
|
|
||||||
|
"""
|
||||||
dn = create_ldap_user.delay(
|
dn = create_ldap_user.delay(
|
||||||
self.username, self.uid, self.group.gid, self.gecos,
|
self.username, self.uid, self.group.gid, self.gecos,
|
||||||
self.homedir, self.shell, password=None).get()
|
self.homedir, self.shell, password=None).get()
|
||||||
sftp_dir = setup_file_sftp_userdir.delay(self.username).get()
|
sftp_dir = setup_file_sftp_userdir.delay(self.username).get()
|
||||||
mail_dir = setup_file_mail_userdir.delay(self.username).get()
|
mail_dir = setup_file_mail_userdir.delay(self.username).get()
|
||||||
logger.info(
|
_LOGGER.info(
|
||||||
"created user %(user)s with LDAP dn %(dn)s, home directory "
|
"created user %(user)s with LDAP dn %(dn)s, home directory "
|
||||||
"%(homedir)s and mail base directory %(maildir)s.", {
|
"%(homedir)s and mail base directory %(maildir)s.", {
|
||||||
'user': self, 'dn': dn,
|
'user': self, 'dn': dn,
|
||||||
|
@ -176,6 +276,16 @@ class User(TimeStampedModel, models.Model):
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def delete(self, *args, **kwargs):
|
def delete(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Delete the user and its groups from LDAP and the database and remove
|
||||||
|
the user's directories.
|
||||||
|
|
||||||
|
:param args: positional arguments to be passed on to
|
||||||
|
:py:meth:`django.db.Model.delete`
|
||||||
|
:param kwargs: keyword arguments to be passed on to
|
||||||
|
:py:meth:`django.db.Model.delete`
|
||||||
|
|
||||||
|
"""
|
||||||
delete_file_mail_userdir.delay(self.username).get()
|
delete_file_mail_userdir.delay(self.username).get()
|
||||||
delete_file_sftp_userdir.delay(self.username).get()
|
delete_file_sftp_userdir.delay(self.username).get()
|
||||||
for group in [ag.group for ag in self.additionalgroup_set.all()]:
|
for group in [ag.group for ag in self.additionalgroup_set.all()]:
|
||||||
|
@ -187,9 +297,23 @@ class User(TimeStampedModel, models.Model):
|
||||||
|
|
||||||
|
|
||||||
class ShadowManager(models.Manager):
|
class ShadowManager(models.Manager):
|
||||||
|
"""
|
||||||
|
Manager class for :py:class:`osusers.models.Shadow`.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def create_shadow(self, user, password):
|
def create_shadow(self, user, password):
|
||||||
|
"""
|
||||||
|
Create a new shadow instance with typical Linux settings for the given
|
||||||
|
user with the given password.
|
||||||
|
|
||||||
|
:param user: :py:class:`User <osusers.models.User>` instance
|
||||||
|
:param str password: the password
|
||||||
|
:return: new Shadow instance
|
||||||
|
:rtype: :py:class:`osusers.models.Shadow` instance
|
||||||
|
|
||||||
|
"""
|
||||||
changedays = (timezone.now().date() - date(1970, 1, 1)).days
|
changedays = (timezone.now().date() - date(1970, 1, 1)).days
|
||||||
shadow = self.create(
|
shadow = self.create(
|
||||||
user=user, changedays=changedays,
|
user=user, changedays=changedays,
|
||||||
|
@ -203,6 +327,11 @@ class ShadowManager(models.Manager):
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Shadow(TimeStampedModel, models.Model):
|
class Shadow(TimeStampedModel, models.Model):
|
||||||
|
"""
|
||||||
|
This entity class corresponds to an operating system user's shadow file
|
||||||
|
entry.
|
||||||
|
|
||||||
|
"""
|
||||||
user = models.OneToOneField(User, primary_key=True, verbose_name=_('User'))
|
user = models.OneToOneField(User, primary_key=True, verbose_name=_('User'))
|
||||||
passwd = models.CharField(_('Encrypted password'), max_length=128)
|
passwd = models.CharField(_('Encrypted password'), max_length=128)
|
||||||
changedays = models.PositiveSmallIntegerField(
|
changedays = models.PositiveSmallIntegerField(
|
||||||
|
@ -245,11 +374,21 @@ class Shadow(TimeStampedModel, models.Model):
|
||||||
return 'for user {0}'.format(self.user)
|
return 'for user {0}'.format(self.user)
|
||||||
|
|
||||||
def set_password(self, password):
|
def set_password(self, password):
|
||||||
|
"""
|
||||||
|
Set and encrypt the password.
|
||||||
|
|
||||||
|
:param str password: the password
|
||||||
|
"""
|
||||||
self.passwd = sha512_crypt.encrypt(password)
|
self.passwd = sha512_crypt.encrypt(password)
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class AdditionalGroup(TimeStampedModel, models.Model):
|
class AdditionalGroup(TimeStampedModel, models.Model):
|
||||||
|
"""
|
||||||
|
This entity class corresponds to additional group assignments for an
|
||||||
|
:py:class:`operating system user <osusers.models.User>`.
|
||||||
|
|
||||||
|
"""
|
||||||
user = models.ForeignKey(User)
|
user = models.ForeignKey(User)
|
||||||
group = models.ForeignKey(Group)
|
group = models.ForeignKey(Group)
|
||||||
|
|
||||||
|
@ -258,21 +397,45 @@ class AdditionalGroup(TimeStampedModel, models.Model):
|
||||||
verbose_name = _('Additional group')
|
verbose_name = _('Additional group')
|
||||||
verbose_name_plural = _('Additional groups')
|
verbose_name_plural = _('Additional groups')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '{0} in {1}'.format(self.user, self.group)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
"""
|
||||||
|
Ensure that the assigned group is different from the user's primary
|
||||||
|
group.
|
||||||
|
|
||||||
|
"""
|
||||||
if self.user.group == self.group:
|
if self.user.group == self.group:
|
||||||
raise ValidationError(CANNOT_USE_PRIMARY_GROUP_AS_ADDITIONAL)
|
raise ValidationError(CANNOT_USE_PRIMARY_GROUP_AS_ADDITIONAL)
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Persists the group assignment to LDAP and the database.
|
||||||
|
|
||||||
|
:param args: positional arguments to be passed on to
|
||||||
|
:py:meth:`django.db.Model.save`
|
||||||
|
:param kwargs: keyword arguments to be passed on to
|
||||||
|
:py:meth:`django.db.Model.save`
|
||||||
|
:return: this instance
|
||||||
|
:rtype: :py:class:`AdditionalGroup <osusers.models.AdditionalGroup>`
|
||||||
|
|
||||||
|
"""
|
||||||
add_ldap_user_to_group.delay(
|
add_ldap_user_to_group.delay(
|
||||||
self.user.username, self.group.groupname).get()
|
self.user.username, self.group.groupname).get()
|
||||||
super(AdditionalGroup, self).save(*args, **kwargs)
|
return super(AdditionalGroup, self).save(*args, **kwargs)
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def delete(self, *args, **kwargs):
|
def delete(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Delete the group assignment from LDAP and the database.
|
||||||
|
|
||||||
|
:param args: positional arguments to be passed on to
|
||||||
|
:py:meth:`django.db.Model.delete`
|
||||||
|
:param kwargs: keyword arguments to be passed on to
|
||||||
|
:py:meth:`django.db.Model.delete`
|
||||||
|
"""
|
||||||
remove_ldap_user_from_group.delay(
|
remove_ldap_user_from_group.delay(
|
||||||
self.user.username, self.group.groupname).get()
|
self.user.username, self.group.groupname).get()
|
||||||
super(AdditionalGroup, self).delete(*args, **kwargs)
|
super(AdditionalGroup, self).delete(*args, **kwargs)
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return '{0} in {1}'.format(self.user, self.group)
|
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
"""
|
||||||
|
This module defines task stubs for the tasks implemented on the `Celery`_
|
||||||
|
workers.
|
||||||
|
|
||||||
|
.. _Celery: http://www.celeryproject.org/
|
||||||
|
|
||||||
|
"""
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
|
@ -5,59 +12,182 @@ from celery import shared_task
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def create_ldap_group(groupname, gid, descr):
|
def create_ldap_group(groupname, gid, descr):
|
||||||
pass
|
"""
|
||||||
|
This task creates an :py:class:`LDAP group <ldapentities.models.LdapGroup>`
|
||||||
|
if it does not exist yet.
|
||||||
|
|
||||||
|
If a group with the given name exists its group id and description
|
||||||
|
attributes are updated.
|
||||||
|
|
||||||
|
:param str groupname: the group name
|
||||||
|
:param int gid: the group id
|
||||||
|
:param str descr: description text for the group
|
||||||
|
:return: the distinguished name of the group
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def create_ldap_user(username, uid, gid, gecos, homedir, shell, password):
|
def create_ldap_user(username, uid, gid, gecos, homedir, shell, password):
|
||||||
pass
|
"""
|
||||||
|
This task creates an :py:class:`LDAP user <ldapentities.models.LdapUser>`
|
||||||
|
if it does not exist yet.
|
||||||
|
|
||||||
|
The task is rejected if the primary group of the user is not defined.
|
||||||
|
|
||||||
|
The user's fields are updated if the user already exists.
|
||||||
|
|
||||||
|
:param str username: the user name
|
||||||
|
:param int uid: the user id
|
||||||
|
:param int gid: the user's primary group's id
|
||||||
|
:param str gecos: the text for the GECOS field
|
||||||
|
:param str homedir: the user's home directory
|
||||||
|
:param str shell: the user's login shell
|
||||||
|
:param str or None password: the clear text password, if :py:const:`None`
|
||||||
|
is passed the password is not touched
|
||||||
|
:raises celery.exceptions.Reject: if the specified primary group does not
|
||||||
|
exist
|
||||||
|
:return: the distinguished name of the user
|
||||||
|
:rtype: str
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def add_ldap_user_to_group(username, groupname):
|
def add_ldap_user_to_group(username, groupname):
|
||||||
pass
|
"""
|
||||||
|
This task adds the specified user to the given group.
|
||||||
|
|
||||||
|
This task does nothing if the user is already member of the group.
|
||||||
|
|
||||||
|
:param str username: the user name
|
||||||
|
:param str groupname: the group name
|
||||||
|
:raises celery.exceptions.Retry: if the user does not exist yet,
|
||||||
|
:py:func:`create_ldap_user` should be called before
|
||||||
|
:return: True if the user has been added to the group otherwise False
|
||||||
|
:rtype: boolean
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def remove_ldap_user_from_group(username, groupname):
|
def remove_ldap_user_from_group(username, groupname):
|
||||||
pass
|
"""
|
||||||
|
This task removes the given user from the given group.
|
||||||
|
|
||||||
|
:param str username: the user name
|
||||||
|
:param str groupname: the group name
|
||||||
|
:return: True if the user has been removed, False otherwise
|
||||||
|
:rtype: boolean
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def delete_ldap_user(username):
|
def delete_ldap_user(username):
|
||||||
pass
|
"""
|
||||||
|
This task deletes the given user.
|
||||||
|
|
||||||
|
:param str username: the user name
|
||||||
|
:return: True if the user has been deleted, False otherwise
|
||||||
|
:rtype: boolean
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def delete_ldap_group_if_empty(groupname):
|
def delete_ldap_group_if_empty(groupname):
|
||||||
pass
|
"""
|
||||||
|
This task deletes the given group.
|
||||||
|
|
||||||
|
:param str groupname: the group name
|
||||||
|
:return: True if the user has been deleted, False otherwise
|
||||||
|
:rtype: boolean
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def setup_file_sftp_userdir(username):
|
def setup_file_sftp_userdir(username):
|
||||||
pass
|
"""
|
||||||
|
This task creates the home directory for an SFTP user if it does not exist
|
||||||
|
yet.
|
||||||
|
|
||||||
|
:param str username: the user name
|
||||||
|
:raises Exception: if the SFTP directory of the user cannot be created
|
||||||
|
:return: the created directory name
|
||||||
|
:rtype: str
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def delete_file_sftp_userdir(username):
|
def delete_file_sftp_userdir(username):
|
||||||
pass
|
"""
|
||||||
|
This task recursively deletes the home directory of an SFTP user if it
|
||||||
|
does not exist yet.
|
||||||
|
|
||||||
|
:param str username: the user name
|
||||||
|
:raises Exception: if the SFTP directory of the user cannot be removed
|
||||||
|
:return: the removed directory name
|
||||||
|
:rtype: str
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def setup_file_mail_userdir(username):
|
def setup_file_mail_userdir(username):
|
||||||
pass
|
"""
|
||||||
|
This task creates the mail base directory for a user if it does not exist
|
||||||
|
yet.
|
||||||
|
|
||||||
|
:param str username: the user name
|
||||||
|
:raises Exception: if the mail base directory for the user cannot be
|
||||||
|
created
|
||||||
|
:return: the created directory name
|
||||||
|
:rtype: str
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def delete_file_mail_userdir(username):
|
def delete_file_mail_userdir(username):
|
||||||
pass
|
"""
|
||||||
|
This task recursively deletes the mail base directory for a user if it
|
||||||
|
does not exist yet.
|
||||||
|
|
||||||
|
:param str username: the user name
|
||||||
|
:raises Exception: if the mail base directory of the user cannot be removed
|
||||||
|
:return: the removed directory name
|
||||||
|
:rtype: str
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def create_file_mailbox(username, mailboxname):
|
def create_file_mailbox(username, mailboxname):
|
||||||
pass
|
"""
|
||||||
|
This task creates a new mailbox directory for the given user and mailbox
|
||||||
|
name.
|
||||||
|
|
||||||
|
:param str username: the user name
|
||||||
|
:param str mailboxname: the mailbox name
|
||||||
|
:raises GVAFileException: if the mailbox directory cannot be created
|
||||||
|
:return: the created mailbox directory name
|
||||||
|
:rtype: str
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def delete_file_mailbox(username, mailboxname):
|
def delete_file_mailbox(username, mailboxname):
|
||||||
pass
|
"""
|
||||||
|
This task deletes the given mailbox of the given user.
|
||||||
|
|
||||||
|
:param str username: the user name
|
||||||
|
:param str mailboxname: the mailbox name
|
||||||
|
:raises GVAFileException: if the mailbox directory cannot be deleted
|
||||||
|
:return: the deleted mailbox directory name
|
||||||
|
:rtype: str
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
Loading…
Reference in a new issue