finish initial documentation
This commit is contained in:
commit
60fc992191
28 changed files with 425 additions and 78 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -39,3 +39,4 @@ Thumbs.db
|
||||||
Desktop.ini
|
Desktop.ini
|
||||||
|
|
||||||
.ropeproject
|
.ropeproject
|
||||||
|
_build/
|
||||||
|
|
49
README.rst
49
README.rst
|
@ -4,49 +4,8 @@ gvaldap
|
||||||
|
|
||||||
This is the GNUViech Admin LDAP administration tool project.
|
This is the GNUViech Admin LDAP administration tool project.
|
||||||
|
|
||||||
Working Environment
|
GNUViech Admin is a suite of tools for server management used for hosting
|
||||||
===================
|
customer management at `Jan Dittberner IT-Consulting & -Solutions
|
||||||
|
<http://www.gnuviech-server.de>`_.
|
||||||
|
|
||||||
You have several options in setting up your working environment. We recommend
|
Read the :doc:`Installation instructions <install>` to get started locally.
|
||||||
using virtualenv to separate the dependencies of your project from your
|
|
||||||
system's python environment. If on Linux or Mac OS X, you can also use
|
|
||||||
virtualenvwrapper to help manage multiple virtualenvs across different
|
|
||||||
projects.
|
|
||||||
|
|
||||||
Virtualenv Only
|
|
||||||
---------------
|
|
||||||
|
|
||||||
First, make sure you are using virtualenv (http://www.virtualenv.org). Once
|
|
||||||
that's installed, create your virtualenv::
|
|
||||||
|
|
||||||
$ virtualenv --distribute gvaldap
|
|
||||||
|
|
||||||
You will also need to ensure that the virtualenv has the project directory
|
|
||||||
added to the path. Adding the project directory will allow `django-admin.py` to
|
|
||||||
be able to change settings using the `--settings` flag.
|
|
||||||
|
|
||||||
Virtualenv with virtualenvwrapper
|
|
||||||
------------------------------------
|
|
||||||
|
|
||||||
In Linux and Mac OSX, you can install virtualenvwrapper
|
|
||||||
(http://virtualenvwrapper.readthedocs.org/en/latest/), which will take care of
|
|
||||||
managing your virtual environments and adding the project path to the
|
|
||||||
`site-directory` for you::
|
|
||||||
|
|
||||||
$ mkdir gvaldap
|
|
||||||
$ mkvirtualenv -a gvaldap gvaldap-dev
|
|
||||||
$ cd gvaldap && add2virtualenv `pwd`
|
|
||||||
|
|
||||||
|
|
||||||
Installation of Dependencies
|
|
||||||
=============================
|
|
||||||
|
|
||||||
Depending on where you are installing dependencies:
|
|
||||||
|
|
||||||
In development::
|
|
||||||
|
|
||||||
$ pip install -r requirements/local.txt
|
|
||||||
|
|
||||||
For production::
|
|
||||||
|
|
||||||
$ pip install -r requirements.txt
|
|
||||||
|
|
4
docs/changelog.rst
Normal file
4
docs/changelog.rst
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
Changelog
|
||||||
|
=========
|
||||||
|
|
||||||
|
* :feature:`-` intial support for creating LDAP users and groups
|
97
docs/code.rst
Normal file
97
docs/code.rst
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
==================
|
||||||
|
Code documentation
|
||||||
|
==================
|
||||||
|
|
||||||
|
.. index:: Django
|
||||||
|
|
||||||
|
gvaldap is implemented as `Django`_ project and provides some `Celery`_ tasks.
|
||||||
|
|
||||||
|
.. _Django: https://www.djangoproject.com/
|
||||||
|
.. _Celery: http://www.celeryproject.org/
|
||||||
|
|
||||||
|
|
||||||
|
The project module :py:mod:`gvaldap`
|
||||||
|
====================================
|
||||||
|
|
||||||
|
.. automodule:: gvaldap
|
||||||
|
|
||||||
|
|
||||||
|
:py:mod:`gvaldap.celery`
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
.. automodule:: gvaldap.celery
|
||||||
|
:members:
|
||||||
|
|
||||||
|
|
||||||
|
:py:mod:`gvaldap.urls`
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
.. automodule:: gvaldap.urls
|
||||||
|
|
||||||
|
|
||||||
|
:py:mod:`gvaldap.wsgi`
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
.. automodule:: gvaldap.wsgi
|
||||||
|
:members:
|
||||||
|
|
||||||
|
|
||||||
|
:py:mod:`gvaldap.settings`
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
.. automodule:: gvaldap.settings
|
||||||
|
|
||||||
|
:py:mod:`gvaldap.settings.base`
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. automodule:: gvaldap.settings.base
|
||||||
|
:members:
|
||||||
|
|
||||||
|
:py:mod:`gvaldap.settings.local`
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. automodule:: gvaldap.settings.local
|
||||||
|
|
||||||
|
:py:mod:`gvaldap.settings.production`
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. automodule:: gvaldap.settings.production
|
||||||
|
|
||||||
|
:py:mod:`gvaldap.settings.test`
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. automodule:: gvaldap.settings.test
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:py:mod:`ldapentities` app
|
||||||
|
==========================
|
||||||
|
|
||||||
|
.. automodule:: ldapentities
|
||||||
|
|
||||||
|
|
||||||
|
:py:mod:`ldapenties.admin`
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
.. automodule:: ldapentities.admin
|
||||||
|
:members:
|
||||||
|
|
||||||
|
|
||||||
|
:py:mod:`ldapenties.models`
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
.. automodule:: ldapentities.models
|
||||||
|
:members:
|
||||||
|
|
||||||
|
|
||||||
|
:py:mod:`osusers` app
|
||||||
|
=====================
|
||||||
|
|
||||||
|
.. automodule:: osusers
|
||||||
|
|
||||||
|
:py:mod:`osusers.tasks`
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
.. automodule:: osusers.tasks
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
11
docs/conf.py
11
docs/conf.py
|
@ -12,12 +12,13 @@
|
||||||
# All configuration values have a default; values that are commented out
|
# All configuration values have a default; values that are commented out
|
||||||
# serve to show the default.
|
# serve to show the default.
|
||||||
|
|
||||||
#import sys, os
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
# 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
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
#sys.path.insert(0, os.path.abspath('.'))
|
sys.path.insert(0, os.path.abspath(os.path.join('..', 'gvaldap')))
|
||||||
|
|
||||||
# -- General configuration -----------------------------------------------------
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
|
@ -26,11 +27,15 @@
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
extensions = []
|
extensions = ['releases', 'sphinx.ext.autodoc', 'celery.contrib.sphinx']
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ['_templates']
|
templates_path = ['_templates']
|
||||||
|
|
||||||
|
releases_issue_uri = 'https://dev.gnuviech-server.de/gvaldap/ticket/%s'
|
||||||
|
|
||||||
|
releases_release_uri = 'https://dev.gnuviech-server.de/gvaldap/milestone/%s'
|
||||||
|
|
||||||
# The suffix of source filenames.
|
# The suffix of source filenames.
|
||||||
source_suffix = '.rst'
|
source_suffix = '.rst'
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
Deploy
|
Deploy
|
||||||
========
|
========
|
||||||
|
|
||||||
This is where you describe how the project is deployed in production.
|
The production deployment for gvaldap is performed using saltstack and consists
|
||||||
|
of the following steps:
|
||||||
|
|
||||||
|
* installation of native dependencies
|
||||||
|
* setup of a virtualenv
|
||||||
|
* installation of gvaldap production dependencies inside the virtualenv
|
||||||
|
* setup of celery worker under control of supervisord
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
You can adapt this file completely to your liking, but it should at least
|
You can adapt this file completely to your liking, but it should at least
|
||||||
contain the root `toctree` directive.
|
contain the root `toctree` directive.
|
||||||
|
|
||||||
|
.. include:: ../README.rst
|
||||||
|
|
||||||
Welcome to gvaldap's documentation!
|
Welcome to gvaldap's documentation!
|
||||||
====================================
|
====================================
|
||||||
|
|
||||||
|
@ -13,8 +15,8 @@ Contents:
|
||||||
|
|
||||||
install
|
install
|
||||||
deploy
|
deploy
|
||||||
tests
|
code
|
||||||
|
changelog
|
||||||
|
|
||||||
|
|
||||||
Indices and tables
|
Indices and tables
|
||||||
|
|
|
@ -1,4 +1,84 @@
|
||||||
|
.. index:: installation
|
||||||
|
|
||||||
|
=======
|
||||||
Install
|
Install
|
||||||
=======
|
=======
|
||||||
|
|
||||||
This is where you write how to get a new laptop to run this project.
|
Working Environment
|
||||||
|
===================
|
||||||
|
|
||||||
|
You have several options in setting up your working environment. We recommend
|
||||||
|
using virtualenv to separate the dependencies of your project from your
|
||||||
|
system's python environment. If on Linux or Mac OS X, you can also use
|
||||||
|
virtualenvwrapper to help manage multiple virtualenvs across different
|
||||||
|
projects.
|
||||||
|
|
||||||
|
.. index:: virtualenv
|
||||||
|
|
||||||
|
Virtualenv Only
|
||||||
|
---------------
|
||||||
|
|
||||||
|
First, make sure you are using `virtualenv`_. Once that's installed, create
|
||||||
|
your virtualenv:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
$ virtualenv --distribute gvaldap
|
||||||
|
|
||||||
|
.. _virtualenv: https://virtualenv.pypa.io/en/latest/
|
||||||
|
|
||||||
|
You will also need to ensure that the virtualenv has the project directory
|
||||||
|
added to the path. Adding the project directory will allow `django-admin.py` to
|
||||||
|
be able to change settings using the `--settings` flag.
|
||||||
|
|
||||||
|
.. index:: virtualenvwrapper
|
||||||
|
|
||||||
|
Virtualenv with virtualenvwrapper
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
In Linux and Mac OSX, you can install `virtualenvwrapper
|
||||||
|
<http://virtualenvwrapper.readthedocs.org/en/latest/>`_, which will take care
|
||||||
|
of managing your virtual environments and adding the project path to the
|
||||||
|
`site-directory` for you:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
$ mkdir gvaldap
|
||||||
|
$ mkvirtualenv -a gvaldap gvaldap-dev
|
||||||
|
$ cd gvaldap && add2virtualenv `pwd`
|
||||||
|
|
||||||
|
|
||||||
|
.. index:: pip, requirements, dependencies
|
||||||
|
|
||||||
|
Installation of Dependencies
|
||||||
|
=============================
|
||||||
|
|
||||||
|
Depending on where you are installing dependencies:
|
||||||
|
|
||||||
|
In development:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
$ pip install -r requirements/local.txt
|
||||||
|
|
||||||
|
For production:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
$ pip install -r requirements.txt
|
||||||
|
|
||||||
|
.. index:: celery, worker, ldap queue
|
||||||
|
|
||||||
|
Running the Celery worker
|
||||||
|
=========================
|
||||||
|
|
||||||
|
gvaldap uses the `Celery`_ distributed task queue system. The gvaldap logix is
|
||||||
|
executed by a celery worker. After all dependencies are installed you can go
|
||||||
|
into the gvaldap directory and run the celery worker with:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
$ cd gvaldap
|
||||||
|
$ celery -A gvaldap worker -Q ldap -l info
|
||||||
|
|
||||||
|
.. _Celery: http://www.celeryproject.org/
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
"""
|
||||||
|
This is the gvaldap project module.
|
||||||
|
"""
|
|
@ -1,3 +1,9 @@
|
||||||
|
"""
|
||||||
|
This module defines the Celery_ app for gvaldap.
|
||||||
|
|
||||||
|
.. _Celery: http://www.celeryproject.org/
|
||||||
|
|
||||||
|
"""
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
@ -10,12 +16,8 @@ os.environ.setdefault('DJANGO_SETTINGS_MODULE',
|
||||||
'gvaldap.settings.production')
|
'gvaldap.settings.production')
|
||||||
|
|
||||||
|
|
||||||
|
#: The Celery application
|
||||||
app = Celery('gvaldap')
|
app = Celery('gvaldap')
|
||||||
|
|
||||||
app.config_from_object('django.conf:settings')
|
app.config_from_object('django.conf:settings')
|
||||||
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
|
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
|
||||||
|
|
||||||
|
|
||||||
@app.task(bind=True)
|
|
||||||
def debug_task(self):
|
|
||||||
print('Request: {0!r}'.format(self.request))
|
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
|
"""
|
||||||
|
This module contains settings for various environments.
|
||||||
|
"""
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
"""Common settings and globals."""
|
# -*- coding: utf-8 -*-
|
||||||
# pymode:lint_ignore=E501
|
# pymode:lint_ignore=E501
|
||||||
|
"""
|
||||||
|
Common settings and globals.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
from os.path import abspath, basename, dirname, join, normpath
|
from os.path import abspath, basename, dirname, join, normpath
|
||||||
|
@ -12,7 +16,14 @@ from django.core.exceptions import ImproperlyConfigured
|
||||||
|
|
||||||
|
|
||||||
def get_env_setting(setting):
|
def get_env_setting(setting):
|
||||||
""" Get the environment setting or return exception """
|
"""
|
||||||
|
Get the environment setting or return exception.
|
||||||
|
|
||||||
|
:param str setting: name of an environment setting
|
||||||
|
:raises ImproperlyConfigured: if the environment setting is not defined
|
||||||
|
:return: environment setting value
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
return environ[setting]
|
return environ[setting]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
"""Development settings and globals."""
|
|
||||||
# pymode:lint_ignore=W0401,E501
|
# pymode:lint_ignore=W0401,E501
|
||||||
|
"""
|
||||||
|
Development settings and globals based on :py:mod:`gvaldap.settings.base`.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
"""Production settings and globals."""
|
|
||||||
# pymode:lint_ignore=W0401,E501
|
# pymode:lint_ignore=W0401,E501
|
||||||
|
"""
|
||||||
|
Production settings and globals based on :py:mod:`gvaldap.settings.base`.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
# pymode:lint_ignore=W0401
|
# pymode:lint_ignore=W0401
|
||||||
|
"""
|
||||||
|
Test settings based on :py:mod:`gvaldap.settings.base`.
|
||||||
|
|
||||||
|
"""
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from .base import *
|
from .base import *
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
"""
|
||||||
|
This module defines the main URLConf for gvaldap.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
from django.conf.urls import patterns, include, url
|
from django.conf.urls import patterns, include, url
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
|
@ -26,10 +26,10 @@ path.append(SITE_ROOT)
|
||||||
# os.environ["DJANGO_SETTINGS_MODULE"] = "jajaja.settings"
|
# os.environ["DJANGO_SETTINGS_MODULE"] = "jajaja.settings"
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gvaldap.settings.production")
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gvaldap.settings.production")
|
||||||
|
|
||||||
|
from django.core.wsgi import get_wsgi_application
|
||||||
# This application object is used by any WSGI server configured to use this
|
# This application object is used by any WSGI server configured to use this
|
||||||
# file. This includes Django's development server, if the WSGI_APPLICATION
|
# file. This includes Django's development server, if the WSGI_APPLICATION
|
||||||
# setting points here.
|
# setting points here.
|
||||||
from django.core.wsgi import get_wsgi_application
|
|
||||||
application = get_wsgi_application()
|
application = get_wsgi_application()
|
||||||
|
|
||||||
# Apply WSGI middleware here.
|
# Apply WSGI middleware here.
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
"""
|
||||||
|
This app takes care of managing LDAP entities, at the moment these are:
|
||||||
|
|
||||||
|
* LDAP groups (:py:class:`ldapentities.models.LdapGroup`).
|
||||||
|
* LDAP users (:py:class:`ldapentities.models.LdapUser`)
|
||||||
|
|
||||||
|
"""
|
|
@ -1,3 +1,10 @@
|
||||||
|
"""
|
||||||
|
Admin classes for easy `django admin`_ based administration of LDAP entities.
|
||||||
|
|
||||||
|
.. _django admin: https://docs.djangoproject.com/en/dev/ref/contrib/admin/
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
|
@ -7,12 +14,22 @@ from .models import (
|
||||||
|
|
||||||
|
|
||||||
class LdapGroupAdmin(admin.ModelAdmin):
|
class LdapGroupAdmin(admin.ModelAdmin):
|
||||||
|
"""
|
||||||
|
Admin class for :py:class:`LDAP group <ldapentities.models.LdapGroup>`
|
||||||
|
entities.
|
||||||
|
|
||||||
|
"""
|
||||||
exclude = ['dn', 'members']
|
exclude = ['dn', 'members']
|
||||||
list_display = ['name', 'gid']
|
list_display = ['name', 'gid']
|
||||||
search_fields = ['name']
|
search_fields = ['name']
|
||||||
|
|
||||||
|
|
||||||
class LdapUserAdmin(admin.ModelAdmin):
|
class LdapUserAdmin(admin.ModelAdmin):
|
||||||
|
"""
|
||||||
|
Admin class for :py:class:`LDAP user <ldapentities.models.LdapUser>`
|
||||||
|
entities.
|
||||||
|
|
||||||
|
"""
|
||||||
exclude = ['dn', 'password']
|
exclude = ['dn', 'password']
|
||||||
list_display = ['username', 'uid']
|
list_display = ['username', 'uid']
|
||||||
search_fields = ['username']
|
search_fields = ['username']
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
|
"""
|
||||||
|
This module defines models for LDAP entities.
|
||||||
|
|
||||||
|
The models are based on :py:class:`ldapmodels.Model` from `django-ldapdb`_.
|
||||||
|
|
||||||
|
.. _django-ldapdb: https://github.com/jlaine/django-ldapdb#readme
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
from ldapdb.models.fields import (
|
from ldapdb.models.fields import (
|
||||||
|
@ -13,44 +22,87 @@ from passlib.hash import ldap_salted_sha1
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class LdapGroup(ldapmodels.Model):
|
class LdapGroup(ldapmodels.Model):
|
||||||
"""
|
"""
|
||||||
Class for representing an LDAP group entity.
|
Class for representing an LDAP group entity with objectClass `posixGroup`.
|
||||||
|
|
||||||
|
.. seealso:: :rfc:`2307#section-4`
|
||||||
|
|
||||||
|
.. py:attribute:: base_dn
|
||||||
|
|
||||||
|
a string containing the LDAP base distinguished name
|
||||||
|
|
||||||
|
.. py:attribute:: members
|
||||||
|
|
||||||
|
contains the list of `memberUid` attributes
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# LDAP meta-data
|
# LDAP meta-data
|
||||||
base_dn = settings.GROUP_BASE_DN
|
base_dn = settings.GROUP_BASE_DN
|
||||||
|
#: list of object classes
|
||||||
object_classes = ['posixGroup']
|
object_classes = ['posixGroup']
|
||||||
|
|
||||||
# posixGroup attributes
|
# posixGroup attributes
|
||||||
|
#: group id (`gidNumber`)
|
||||||
gid = IntegerField(db_column='gidNumber', unique=True)
|
gid = IntegerField(db_column='gidNumber', unique=True)
|
||||||
|
#: group name (`cn`)
|
||||||
name = CharField(db_column='cn', max_length=200, primary_key=True)
|
name = CharField(db_column='cn', max_length=200, primary_key=True)
|
||||||
|
#: group description (`description`)
|
||||||
description = CharField(db_column='description')
|
description = CharField(db_column='description')
|
||||||
members = ListField(db_column='memberUid', blank=True)
|
members = ListField(db_column='memberUid', blank=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
"""
|
||||||
|
Get a string representation of this LDAP group.
|
||||||
|
"""
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class LdapUser(ldapmodels.Model):
|
class LdapUser(ldapmodels.Model):
|
||||||
"""
|
"""
|
||||||
Class for representing an LDAP user entity.
|
Class for representing an LDAP user entity with objectClasses `account` and
|
||||||
|
`posixAccount`.
|
||||||
|
|
||||||
|
.. seealso:: :rfc:`2307#section-4`, :rfc:`4524#section-3.1`
|
||||||
|
|
||||||
|
.. py:attribute:: base_dn
|
||||||
|
|
||||||
|
a string containing the LDAP base distinguished name
|
||||||
|
|
||||||
"""
|
"""
|
||||||
base_dn = settings.USER_BASE_DN
|
base_dn = settings.USER_BASE_DN
|
||||||
|
#: list of object classes
|
||||||
object_classes = ['account', 'posixAccount']
|
object_classes = ['account', 'posixAccount']
|
||||||
|
|
||||||
# posixAccount
|
# posixAccount
|
||||||
|
#: user id (`uidNumber`)
|
||||||
uid = IntegerField(db_column='uidNumber', unique=True)
|
uid = IntegerField(db_column='uidNumber', unique=True)
|
||||||
|
#: group id (`gidNumber`) of the user's primary group
|
||||||
group = IntegerField(db_column='gidNumber')
|
group = IntegerField(db_column='gidNumber')
|
||||||
|
#: GECOS field (`gecos`)
|
||||||
gecos = CharField(db_column='gecos')
|
gecos = CharField(db_column='gecos')
|
||||||
|
#: home directory (`homeDirectory`)
|
||||||
home_directory = CharField(db_column='homeDirectory')
|
home_directory = CharField(db_column='homeDirectory')
|
||||||
|
#: login shell (`loginShell`)
|
||||||
login_shell = CharField(db_column='loginShell', default='/bin/bash')
|
login_shell = CharField(db_column='loginShell', default='/bin/bash')
|
||||||
|
#: user name (`uid`)
|
||||||
username = CharField(db_column='uid', primary_key=True)
|
username = CharField(db_column='uid', primary_key=True)
|
||||||
|
#: password (`userPassword`) in an LDAP compatible format
|
||||||
password = CharField(db_column='userPassword')
|
password = CharField(db_column='userPassword')
|
||||||
|
#: common name (`cn`)
|
||||||
common_name = CharField(db_column='cn')
|
common_name = CharField(db_column='cn')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
"""
|
||||||
|
Get a string representation of this LDAP user.
|
||||||
|
"""
|
||||||
return self.username
|
return self.username
|
||||||
|
|
||||||
def set_password(self, password):
|
def set_password(self, password):
|
||||||
|
"""
|
||||||
|
Sets the encrypted password of the user from the given clear text
|
||||||
|
password.
|
||||||
|
|
||||||
|
:param str password: the clear text password
|
||||||
|
|
||||||
|
"""
|
||||||
self.password = ldap_salted_sha1.encrypt(password)
|
self.password = ldap_salted_sha1.encrypt(password)
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
"""
|
||||||
|
This module contains :py:mod:`osusers.tasks`.
|
||||||
|
"""
|
|
@ -1,3 +0,0 @@
|
||||||
from django.contrib import admin
|
|
||||||
|
|
||||||
# Register your models here.
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
"""
|
||||||
|
Empty models module required for Django to accept this as an app.
|
||||||
|
"""
|
|
@ -1,9 +1,17 @@
|
||||||
|
"""
|
||||||
|
This module defines `Celery`_ tasks to manage LDAP entities.
|
||||||
|
|
||||||
|
.. _Celery: http://www.celeryproject.org/
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
from celery.utils.log import get_task_logger
|
from celery.utils.log import get_task_logger
|
||||||
from celery.exceptions import Reject
|
from celery.exceptions import Reject
|
||||||
|
|
||||||
from ldapentities.models import (
|
from ldapentities.models import (
|
||||||
LdapGroup,
|
LdapGroup,
|
||||||
LdapUser,
|
LdapUser,
|
||||||
|
@ -15,6 +23,19 @@ _logger = get_task_logger(__name__)
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def create_ldap_group(groupname, gid, descr):
|
def create_ldap_group(groupname, gid, descr):
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
ldapgroup = LdapGroup.objects.get(name=groupname)
|
ldapgroup = LdapGroup.objects.get(name=groupname)
|
||||||
_logger.info(
|
_logger.info(
|
||||||
|
@ -30,6 +51,28 @@ def create_ldap_group(groupname, gid, descr):
|
||||||
|
|
||||||
@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):
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
ldapuser = LdapUser.objects.get(username=username)
|
ldapuser = LdapUser.objects.get(username=username)
|
||||||
_logger.info(
|
_logger.info(
|
||||||
|
@ -64,6 +107,19 @@ def create_ldap_user(username, uid, gid, gecos, homedir, shell, password):
|
||||||
|
|
||||||
@shared_task(bind=True)
|
@shared_task(bind=True)
|
||||||
def add_ldap_user_to_group(self, username, groupname):
|
def add_ldap_user_to_group(self, username, groupname):
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
ldapgroup = LdapGroup.objects.get(name=groupname)
|
ldapgroup = LdapGroup.objects.get(name=groupname)
|
||||||
ldapuser = LdapUser.objects.get(username=username)
|
ldapuser = LdapUser.objects.get(username=username)
|
||||||
|
@ -80,19 +136,40 @@ def add_ldap_user_to_group(self, username, groupname):
|
||||||
_logger.info('ldap user {0} is already in group {1}'.format(
|
_logger.info('ldap user {0} is already in group {1}'.format(
|
||||||
ldapuser.username, ldapgroup.dn)
|
ldapuser.username, ldapgroup.dn)
|
||||||
)
|
)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def remove_ldap_user_from_group(username, groupname):
|
def remove_ldap_user_from_group(username, groupname):
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
"""
|
||||||
ldapgroup = LdapGroup.objects.get(name=groupname)
|
ldapgroup = LdapGroup.objects.get(name=groupname)
|
||||||
ldapuser = LdapUser.objects.get(username=username)
|
ldapuser = LdapUser.objects.get(username=username)
|
||||||
if ldapuser.username in ldapgroup.members:
|
performdelete = ldapuser.username in ldapgroup.members
|
||||||
|
if performdelete:
|
||||||
ldapgroup.members.remove(ldapuser.username)
|
ldapgroup.members.remove(ldapuser.username)
|
||||||
ldapgroup.save()
|
ldapgroup.save()
|
||||||
|
return performdelete
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def delete_ldap_user(username):
|
def delete_ldap_user(username):
|
||||||
|
"""
|
||||||
|
This task deletes the given user.
|
||||||
|
|
||||||
|
:param str username: the user name
|
||||||
|
:return: True if the user has been deleted, False otherwise
|
||||||
|
:rtype: boolean
|
||||||
|
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
ldapuser = LdapUser.objects.get(username=username)
|
ldapuser = LdapUser.objects.get(username=username)
|
||||||
except LdapUser.DoesNotExist:
|
except LdapUser.DoesNotExist:
|
||||||
|
@ -111,10 +188,20 @@ def delete_ldap_user(username):
|
||||||
ldapgroup.members.remove(ldapuser.username)
|
ldapgroup.members.remove(ldapuser.username)
|
||||||
ldapgroup.save()
|
ldapgroup.save()
|
||||||
ldapuser.delete()
|
ldapuser.delete()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def delete_ldap_group_if_empty(groupname):
|
def delete_ldap_group_if_empty(groupname):
|
||||||
|
"""
|
||||||
|
This task deletes the given group.
|
||||||
|
|
||||||
|
:param str groupname: the group name
|
||||||
|
:return: True if the user has been deleted, False otherwise
|
||||||
|
:rtype: boolean
|
||||||
|
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
ldapgroup = LdapGroup.objects.get(name=groupname)
|
ldapgroup = LdapGroup.objects.get(name=groupname)
|
||||||
except LdapGroup.DoesNotExist:
|
except LdapGroup.DoesNotExist:
|
||||||
|
@ -124,7 +211,9 @@ def delete_ldap_group_if_empty(groupname):
|
||||||
else:
|
else:
|
||||||
if len(ldapgroup.members) == 0:
|
if len(ldapgroup.members) == 0:
|
||||||
ldapgroup.delete()
|
ldapgroup.delete()
|
||||||
|
return True
|
||||||
else:
|
else:
|
||||||
_logger.info('ldap group {0} still has {1} members'.format(
|
_logger.info('ldap group {0} still has {1} members'.format(
|
||||||
ldapgroup.dn, len(ldapgroup.members))
|
ldapgroup.dn, len(ldapgroup.members))
|
||||||
)
|
)
|
||||||
|
return False
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
|
@ -7,7 +7,4 @@ logutils==0.3.3
|
||||||
South==0.8.4
|
South==0.8.4
|
||||||
celery==3.1.11
|
celery==3.1.11
|
||||||
passlib==1.6.2
|
passlib==1.6.2
|
||||||
billiard==3.3.0.17
|
|
||||||
kombu==3.0.16
|
|
||||||
pytz==2014.3
|
|
||||||
pyaml==14.05.7
|
pyaml==14.05.7
|
||||||
|
|
|
@ -4,3 +4,4 @@ coverage==3.7.1
|
||||||
django-debug-toolbar==1.2.1
|
django-debug-toolbar==1.2.1
|
||||||
Sphinx==1.2.2
|
Sphinx==1.2.2
|
||||||
sqlparse==0.1.11
|
sqlparse==0.1.11
|
||||||
|
releases==0.6.1
|
||||||
|
|
Loading…
Reference in a new issue