document osusers code
This commit is contained in:
parent
18e47d73b4
commit
0df67e7154
5 changed files with 430 additions and 27 deletions
|
@ -1,3 +1,7 @@
|
|||
"""
|
||||
This module defines the database models of operating system users.
|
||||
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
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 = _(
|
||||
|
@ -38,8 +42,19 @@ CANNOT_USE_PRIMARY_GROUP_AS_ADDITIONAL = _(
|
|||
|
||||
|
||||
class GroupManager(models.Manager):
|
||||
"""
|
||||
Manager class for :py:class:`osusers.models.Group`.
|
||||
|
||||
"""
|
||||
|
||||
def get_next_gid(self):
|
||||
"""
|
||||
Get the next available group id.
|
||||
|
||||
:returns: group id
|
||||
:rtype: int
|
||||
|
||||
"""
|
||||
q = self.aggregate(models.Max('gid'))
|
||||
if q['gid__max'] is None:
|
||||
return settings.OSUSER_MINGID
|
||||
|
@ -48,6 +63,10 @@ class GroupManager(models.Manager):
|
|||
|
||||
@python_2_unicode_compatible
|
||||
class Group(TimeStampedModel, models.Model):
|
||||
"""
|
||||
This entity class corresponds to an operating system group.
|
||||
|
||||
"""
|
||||
groupname = models.CharField(
|
||||
_('Group name'), max_length=16, unique=True)
|
||||
gid = models.PositiveSmallIntegerField(
|
||||
|
@ -67,34 +86,73 @@ class Group(TimeStampedModel, models.Model):
|
|||
|
||||
@transaction.atomic
|
||||
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)
|
||||
dn = create_ldap_group.delay(
|
||||
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
|
||||
|
||||
@transaction.atomic
|
||||
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()
|
||||
super(Group, self).delete(*args, **kwargs)
|
||||
|
||||
|
||||
class UserManager(models.Manager):
|
||||
"""
|
||||
Manager class for :py:class:`osusers.models.User`.
|
||||
|
||||
"""
|
||||
|
||||
def get_next_uid(self):
|
||||
"""
|
||||
Get the next available user id.
|
||||
|
||||
:return: user id
|
||||
:rtype: int
|
||||
|
||||
"""
|
||||
q = self.aggregate(models.Max('uid'))
|
||||
if q['uid__max'] is None:
|
||||
return settings.OSUSER_MINUID
|
||||
return max(settings.OSUSER_MINUID, q['uid__max'] + 1)
|
||||
|
||||
def get_next_username(self):
|
||||
"""
|
||||
Get the next available user name.
|
||||
|
||||
:return: user name
|
||||
:rtype: str
|
||||
|
||||
"""
|
||||
count = 1
|
||||
usernameformat = "{0}{1:02d}"
|
||||
nextuser = usernameformat.format(settings.OSUSER_USERNAME_PREFIX,
|
||||
count)
|
||||
for user in self.values('username').filter(
|
||||
username__startswith=settings.OSUSER_USERNAME_PREFIX).order_by(
|
||||
'username'):
|
||||
username__startswith=settings.OSUSER_USERNAME_PREFIX
|
||||
).order_by('username'):
|
||||
if user['username'] == nextuser:
|
||||
count += 1
|
||||
nextuser = usernameformat.format(
|
||||
|
@ -107,6 +165,23 @@ class UserManager(models.Manager):
|
|||
def create_user(
|
||||
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()
|
||||
gid = Group.objects.get_next_gid()
|
||||
if username is None:
|
||||
|
@ -126,6 +201,10 @@ class UserManager(models.Manager):
|
|||
|
||||
@python_2_unicode_compatible
|
||||
class User(TimeStampedModel, models.Model):
|
||||
"""
|
||||
This entity class corresponds to an operating system user.
|
||||
|
||||
"""
|
||||
username = models.CharField(
|
||||
_('User name'), max_length=64, unique=True)
|
||||
uid = models.PositiveSmallIntegerField(
|
||||
|
@ -147,6 +226,15 @@ class User(TimeStampedModel, models.Model):
|
|||
|
||||
@transaction.atomic
|
||||
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'):
|
||||
self.shadow.set_password(password)
|
||||
else:
|
||||
|
@ -161,12 +249,24 @@ class User(TimeStampedModel, models.Model):
|
|||
|
||||
@transaction.atomic
|
||||
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(
|
||||
self.username, self.uid, self.group.gid, self.gecos,
|
||||
self.homedir, self.shell, password=None).get()
|
||||
sftp_dir = setup_file_sftp_userdir.delay(self.username).get()
|
||||
mail_dir = setup_file_mail_userdir.delay(self.username).get()
|
||||
logger.info(
|
||||
_LOGGER.info(
|
||||
"created user %(user)s with LDAP dn %(dn)s, home directory "
|
||||
"%(homedir)s and mail base directory %(maildir)s.", {
|
||||
'user': self, 'dn': dn,
|
||||
|
@ -176,6 +276,16 @@ class User(TimeStampedModel, models.Model):
|
|||
|
||||
@transaction.atomic
|
||||
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_sftp_userdir.delay(self.username).get()
|
||||
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):
|
||||
"""
|
||||
Manager class for :py:class:`osusers.models.Shadow`.
|
||||
|
||||
"""
|
||||
|
||||
@transaction.atomic
|
||||
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
|
||||
shadow = self.create(
|
||||
user=user, changedays=changedays,
|
||||
|
@ -203,6 +327,11 @@ class ShadowManager(models.Manager):
|
|||
|
||||
@python_2_unicode_compatible
|
||||
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'))
|
||||
passwd = models.CharField(_('Encrypted password'), max_length=128)
|
||||
changedays = models.PositiveSmallIntegerField(
|
||||
|
@ -245,11 +374,21 @@ class Shadow(TimeStampedModel, models.Model):
|
|||
return 'for user {0}'.format(self.user)
|
||||
|
||||
def set_password(self, password):
|
||||
"""
|
||||
Set and encrypt the password.
|
||||
|
||||
:param str password: the password
|
||||
"""
|
||||
self.passwd = sha512_crypt.encrypt(password)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
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)
|
||||
group = models.ForeignKey(Group)
|
||||
|
||||
|
@ -258,21 +397,45 @@ class AdditionalGroup(TimeStampedModel, models.Model):
|
|||
verbose_name = _('Additional group')
|
||||
verbose_name_plural = _('Additional groups')
|
||||
|
||||
def __str__(self):
|
||||
return '{0} in {1}'.format(self.user, self.group)
|
||||
|
||||
def clean(self):
|
||||
"""
|
||||
Ensure that the assigned group is different from the user's primary
|
||||
group.
|
||||
|
||||
"""
|
||||
if self.user.group == self.group:
|
||||
raise ValidationError(CANNOT_USE_PRIMARY_GROUP_AS_ADDITIONAL)
|
||||
|
||||
@transaction.atomic
|
||||
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(
|
||||
self.user.username, self.group.groupname).get()
|
||||
super(AdditionalGroup, self).save(*args, **kwargs)
|
||||
return super(AdditionalGroup, self).save(*args, **kwargs)
|
||||
|
||||
@transaction.atomic
|
||||
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(
|
||||
self.user.username, self.group.groupname).get()
|
||||
super(AdditionalGroup, self).delete(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return '{0} in {1}'.format(self.user, self.group)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue