-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 55ad54f
Showing
36 changed files
with
3,768 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# Temporary files | ||
.*.swp | ||
*.pyc | ||
*.pyo | ||
|
||
# Build-related files | ||
docs/_build/ | ||
.coverage | ||
.tox | ||
*.egg-info | ||
*.egg | ||
.eggs/ | ||
build/ | ||
dist/ | ||
htmlcov/ | ||
MANIFEST | ||
migrations/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
Copyright (c) The django-ldap-academia-ou-manager project | ||
All rights reserved. | ||
|
||
Redistribution and use in source and binary forms, with or without modification, | ||
are permitted provided that the following conditions are met: | ||
|
||
1. Redistributions of source code must retain the above copyright notice, | ||
this list of conditions and the following disclaimer. | ||
|
||
2. Redistributions in binary form must reproduce the above copyright | ||
notice, this list of conditions and the following disclaimer in the | ||
documentation and/or other materials provided with the distribution. | ||
|
||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | ||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
Django admin LDAP manager for Academia OU | ||
----------------------------------------- | ||
Django Admin manager for Academia Users, usable with a OpenLDAP Server configured with eduPerson, SCHAC (SCHema for ACademia) and Samba schema. | ||
It also needs PPolicy overlay. | ||
|
||
References | ||
---------- | ||
|
||
- [OpenLDAP compatible configuration](https://github.com/peppelinux/ansible-slapd-eduperson2016) | ||
- [eduPerson Schema](https://software.internet2.edu/eduperson/internet2-mace-dir-eduperson-201602.html) | ||
- [SCHAC](https://wiki.refeds.org/display/STAN/SCHAC) | ||
|
||
Requirements | ||
------------ | ||
|
||
- OpenLDAP 2.4.x | ||
- Python 3.x | ||
- Django 2.x | ||
- django-ldapdb (custom repository) | ||
|
||
|
||
Tested on Debian9 and Debian 10. | ||
|
||
Preview | ||
------- | ||
|
||
**Note:** Labels and strings can be localized with .po dictionaries (gettext). See [i18n documentation](https://docs.djangoproject.com/en/dev/topics/i18n/translation/) | ||
|
||
![Alt text](img/search.png) | ||
![Alt text](img/preview.png) | ||
|
||
LDAP Setup | ||
----- | ||
For those who need to setup a LDAP server for development or production use: | ||
```` | ||
pip3 install ansible | ||
git clone https://github.com/peppelinux/ansible-slapd-eduperson2016.git | ||
cd ansible-slapd-eduperson2016 | ||
ansible-playbook -i "localhost," -c local playbook.yml | ||
```` | ||
**Note:** The playbook will backup any existing slapd installations in **backups** folder. | ||
|
||
Setup | ||
----- | ||
|
||
#### Create an environment directory and activate it | ||
```` | ||
apt install python3-dev python3-pip python3-setuptools | ||
pip3 install virtualenv | ||
export PROJ_NAME=django-ldap-academia-ou-manager | ||
export DEST_DIR=$PROJ_NAME.env | ||
virtualenv -p python3 $DEST_DIR | ||
source $dest_dir/bin/activate | ||
pip3 install django | ||
```` | ||
|
||
#### Create a project | ||
```` | ||
django-admin startproject $PROJ_NAME | ||
cd $PROJ_NAME | ||
```` | ||
|
||
#### Install the app | ||
**Note:** It uses a django-ldapdb fork to handle readonly (non editable) fields. This still waiting form merge in official django-ldap repository. | ||
|
||
```` | ||
# pip3 install git+https://github.com/peppelinux/django-ldapdb.git | ||
pip3 install git+https://github.com/peppelinux/django-ldap-academia-ou-manager --process-dependency-link | ||
```` | ||
|
||
#### Edit settings.py | ||
Read settings.py and settingslocal.py in the example folder. | ||
|
||
In settings.py do the following: | ||
|
||
- Add **ldap_peoples** in INSTALLED_APPS; | ||
- import default ldap_peoples settings as follows; | ||
- import default app url as follows; | ||
|
||
#### import default ldap_peoples settings | ||
```` | ||
# settings.py | ||
if 'ldap_peoples' in INSTALLED_APPS: | ||
from ldap_peoples.settings import * | ||
```` | ||
#### import default app url | ||
```` | ||
# urls.py | ||
if 'ldap_peoples' in settings.INSTALLED_APPS: | ||
import ldap_peoples.urls | ||
urlpatterns += path('', include(ldap_peoples.urls, namespace='ldap_peoples')), | ||
```` | ||
|
||
Using the Object Relation Mapper | ||
-------------------------------- | ||
One of the advantage of using the ORM is the possibility to make these kind of queries | ||
to a LDAP database. | ||
|
||
#### User update attributes | ||
```` | ||
from ldap_peoples.models import LdapAcademiaUser | ||
lu = LdapAcademiaUser.objects.get(uid='mario') | ||
# as multivalue | ||
lu.eduPersonAffiliation.append('alumn') | ||
lu.save() | ||
lu.set_password('secr3tP4ss20rd') | ||
# search into multivalue field | ||
other_lus = LdapAcademiaUser.objects.filter(mail_contains='unical') | ||
```` | ||
|
||
#### User creation example | ||
```` | ||
# user creation | ||
import datetime | ||
d = {'cn': 'pedppe', | ||
'displayName': 'peppde Rossi', | ||
'eduPersonAffiliation': ['faculty', 'member'], | ||
'eduPersonEntitlement': ['urn:mace:terena.org:tcs:escience-user', | ||
'urn:mace:terena.org:tcs:personal-user'], | ||
'eduPersonOrcid': '', | ||
'eduPersonPrincipalName': 'grodsfssi@unical', | ||
'eduPersonScopedAffiliation': ['[email protected]', '[email protected]'], | ||
'givenName': 'peppe', | ||
'mail': ['[email protected]', '[email protected]'], | ||
'sambaNTPassword': 'a2137530237ad733fdc26d5d7157d43f', | ||
'schacHomeOrganization': 'testunical.it', | ||
'schacHomeOrganizationType': ['educationInstitution', 'university'], | ||
'schacPersonalUniqueID': ['urn:schac:personalUniqueID:IT:CF:CODICEFISCALEpe3245ppe'], | ||
'schacPlaceOfBirth': '', | ||
'sn': 'grossi', | ||
'telephoneNumber': [], | ||
'uid': 'perrrppe', | ||
'userPassword': '{SHA512}oMKZtxqeWdXrsHkX5wYBo1cKoQPpmnu2WljngOyQd7GQLR3tsxsUV77aWV/k1x13m2ypytR2JmzAdZDjHYSyBg=='} | ||
u = LdapAcademiaUser.objects.create(**d) | ||
u.delete() | ||
```` | ||
|
||
#### Unit test | ||
```` | ||
./manage.py test ldap_peoples.test.LdapAcademiaUserTestCase | ||
```` | ||
|
||
TODO | ||
---- | ||
- form .clean methods could be cleaned with a better OOP refactor on FormFields and Widgets; | ||
|
||
|
||
**Django-ldapdb related** | ||
- We use custom django-ldapdb fork because readonly fields like createTimestamps and other are fautly on save in the official django-ldapdb repo. [See related PR](https://github.com/django-ldapdb/django-ldapdb/pull/185); | ||
- ListFields doesn't handle properly **verbose_name**. It depends on the form class, we use our fork for elude this; | ||
- Aggregate lookup for evaluating min max on records, this come from django-ldapdb; | ||
- too many connection from django-ldapdb backends, fixed in forked django-ldapdb version as follow: | ||
|
||
```` | ||
# backeds.ldap.base#237 | ||
def ensure_connection(self): | ||
super(DatabaseWrapper, self).ensure_connection() | ||
# Do a test bind, which will revive the connection if interrupted, or reconnect | ||
conn_params = self.get_connection_params() | ||
# this creates too many connections to LDAP server! | ||
# try: | ||
# self.connection.simple_bind_s( | ||
# conn_params['bind_dn'], | ||
# conn_params['bind_pw'], | ||
# ) | ||
# print('ensure_connection. try') | ||
# except ldap.SERVER_DOWN: | ||
if not self.connection: | ||
self.connect() | ||
# print('ensure_connection. except') | ||
```` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
""" | ||
Django settings for unical_ict project. | ||
Generated by 'django-admin startproject' using Django 1.11.3. | ||
For more information on this file, see | ||
https://docs.djangoproject.com/en/1.11/topics/settings/ | ||
For the full list of settings and their values, see | ||
https://docs.djangoproject.com/en/1.11/ref/settings/ | ||
""" | ||
|
||
import os | ||
from . settingslocal import * | ||
|
||
#APP_NAME=settingslocal.APP_NAME | ||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) | ||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | ||
|
||
# Quick-start development settings - unsuitable for production | ||
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ | ||
|
||
# SECURITY WARNING: keep the secret key used in production secret! | ||
#SECRET_KEY = settingslocal.SECRET_KEY | ||
|
||
# SECURITY WARNING: don't run with debug turned on in production! | ||
#DEBUG = settingslocal.DEBUG | ||
|
||
#ALLOWED_HOSTS = settingslocal.ALLOWED_HOSTS | ||
|
||
# Application definition | ||
INSTALLED_APPS = [ | ||
'django.contrib.admin', | ||
'django.contrib.auth', | ||
'django.contrib.contenttypes', | ||
'django.contrib.sessions', | ||
'django.contrib.messages', | ||
'django.contrib.staticfiles', | ||
|
||
'rangefilter', | ||
'ldapdb', | ||
'ldap_peoples', | ||
] | ||
|
||
if 'ldap_peoples' in INSTALLED_APPS: | ||
from ldap_peoples.settings import * | ||
# otherwise overload whatever needed... | ||
# import ldap_peoples.settings as ldap_peoples_settings | ||
# LDAP_DATETIME_FORMAT = ldap_peoples_settings.LDAP_DATETIME_FORMAT | ||
# LDAP_DATETIME_MILLISECONDS_FORMAT = ldap_peoples_settings.LDAP_DATETIME_MILLISECONDS_FORMAT | ||
# PPOLICY_PERMANENT_LOCKED_TIME = ldap_peoples_settings.PPOLICY_PERMANENT_LOCKED_TIME | ||
# PPOLICY_PASSWD_MAX_LEN= ldap_peoples_settings.PPOLICY_PASSWD_MAX_LEN | ||
# PPOLICY_PASSWD_MIN_LEN= ldap_peoples_settings.PPOLICY_PASSWD_MIN_LEN | ||
|
||
# PASSWD_FIELDS_MAP = ldap_peoples_settings.PASSWD_FIELDS_MAP | ||
# SECRET_PASSWD_TYPE = ldap_peoples_settings.SECRET_PASSWD_TYPE | ||
# DISABLED_SECRET_TYPES = ldap_peoples_settings.DISABLED_SECRET_TYPES | ||
# DEFAULT_SECRET_TYPE = ldap_peoples_settings.DEFAULT_SECRET_TYPE | ||
# SECRET_FIELD_VALIDATORS = ldap_peoples_settings.SECRET_FIELD_VALIDATORS | ||
|
||
MIDDLEWARE = [ | ||
'django.middleware.security.SecurityMiddleware', | ||
'django.contrib.sessions.middleware.SessionMiddleware', | ||
'django.middleware.common.CommonMiddleware', | ||
'django.middleware.csrf.CsrfViewMiddleware', | ||
'django.contrib.auth.middleware.AuthenticationMiddleware', | ||
'django.contrib.messages.middleware.MessageMiddleware', | ||
'django.middleware.clickjacking.XFrameOptionsMiddleware', | ||
] | ||
|
||
# Messages were getting stored in CookiesStorage, but for some weird reason the Messages in CookiesStorage were getting expired or deleted for the 2nd request | ||
# MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage' | ||
|
||
# GETTEXT LOCALIZATION | ||
MIDDLEWARE.append('django.middleware.locale.LocaleMiddleware') | ||
LOCALE_PATHS = ( | ||
os.path.join(BASE_DIR, "locale"), | ||
) | ||
# | ||
|
||
AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend',] | ||
|
||
LOGIN_URL = '/login' | ||
LOGIN_REDIRECT_URL = '/dashboard' | ||
|
||
TEMPLATES = [ | ||
{ | ||
'BACKEND': 'django.template.backends.django.DjangoTemplates', | ||
'DIRS': ['templates'], | ||
'APP_DIRS': True, | ||
'OPTIONS': { | ||
'context_processors': [ | ||
'django.template.context_processors.debug', | ||
'django.template.context_processors.request', | ||
'django.contrib.auth.context_processors.auth', | ||
'django.contrib.messages.context_processors.messages', | ||
], | ||
}, | ||
}, | ||
] | ||
|
||
WSGI_APPLICATION = 'unical_ict.wsgi.application' | ||
|
||
AUTH_PASSWORD_VALIDATORS = [ | ||
{ | ||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', | ||
}, | ||
{ | ||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', | ||
}, | ||
{ | ||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', | ||
}, | ||
{ | ||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', | ||
}, | ||
] | ||
|
||
|
||
# Internationalization | ||
# https://docs.djangoproject.com/en/1.11/topics/i18n/ | ||
|
||
# LANGUAGE_CODE = settingslocal.LANGUAGE_CODE | ||
# TIME_ZONE = settingslocal.TIME_ZONE | ||
USE_I18N = True | ||
USE_L10N = True | ||
USE_TZ = True | ||
|
||
# shacExpiryDate e shacDateOfBirth validation works on these: | ||
DATE_FORMAT = "%d/%m/%Y" | ||
DATETIME_FORMAT = "{} %H:%M:%S".format(DATE_FORMAT) | ||
|
||
DATE_INPUT_FORMATS = [DATE_FORMAT, "%Y-%m-%d"] | ||
DATETIME_INPUT_FORMATS = ["{} %H:%M:%S".format(i) for i in DATE_INPUT_FORMATS] | ||
|
||
# Static files (CSS, JavaScript, Images) | ||
# https://docs.djangoproject.com/en/1.11/howto/static-files/ | ||
DATA_DIR = os.path.join(BASE_DIR, "data") | ||
STATIC_URL = '/static/' | ||
STATIC_ROOT = os.path.join(DATA_DIR, 'static') | ||
|
||
MEDIA_ROOT = os.path.join(DATA_DIR, 'media') | ||
MEDIA_URL = '/media/' |
Oops, something went wrong.