Compare commits

...

55 commits

Author SHA1 Message Date
Jan Dittberner b3fab53665 Release 0.7.0
- improve Vagrant setup
 -----BEGIN PGP SIGNATURE-----
 
 iQFIBAABCgAyFiEEKHuXKkUYdvdO9493DXkdyNc3wdkFAl6LaqAUHGphbkBkaXR0
 YmVybmVyLmluZm8ACgkQDXkdyNc3wdnreQf/V4c+TpiqJ+Rm7k8MVch/T9+mCran
 8c/b3+4S8u+lstgAjdlwsEL+c1drNLHIjZNKkoeP7FGQ7/kP8jjXFgIfqEfjejZG
 Jy5PN0SzSscCo4rr0+PaeSZ0MziX5bdbTLqAlnazcy67CCCUPUNmE8QBDEaI6sEG
 sv+UMbcVBBsCTFtsnORmLCqfZtEq9V/gi/q9l7JWNUYA2Wq437mfjuNfoax2KKl9
 bOop1Dx4cnfCBzQIOM+hNNWy5x7TZbd49TqTRLeciFRbtAHLfi3QDKankwZFgjWt
 TNKq/praopYzrJ9My9NkZIYvvl+91P0S76M+huSSgoAtcwAU9PgmWDAZFA==
 =8SNS
 -----END PGP SIGNATURE-----

Merge tag '0.7.0'

Release 0.7.0

- improve Vagrant setup
2020-04-06 19:45:09 +02:00
Jan Dittberner d1fc3f80ab Merge branch 'release/0.7.0' into production 2020-04-06 19:44:52 +02:00
Jan Dittberner 190e6e2a4b Bump version, update changelog 2020-04-06 19:44:33 +02:00
Jan Dittberner 562ae2a61a Move some grains to pillars 2020-03-04 17:20:59 +01:00
Jan Dittberner 11b6051bc0 Fix typo in change-vmdebootstrap-default-dhcp.sh 2020-03-04 00:27:49 +01:00
Jan Dittberner a98f6e54e7 Make IP address assignment work with libvirt
The systemd-networkd script in vmdebootstrap that is used to build the
Debian libvirt vagrant boxes is a bit too eager assigning DHCP to
network interfaces. This patch changes the network script to only take
care of the primary network interface (first non loopback).
2020-03-04 00:07:58 +01:00
Jan Dittberner 9f662e3afb Optimize Vagrant setup
- define grains to match optimized gvasalt repository
- define minion configuration in separate file
- drop custome salt/bootstrap.sh
- adapt Vagrantfile to use the custom grains file
- increase memory size of created VM to allow the salt provisioning to
  succeed
2020-03-03 22:11:46 +01:00
Jan Dittberner b54b8577cb Update Vagrant setup to Debian Buster and Python 3 2020-03-03 16:15:18 +01:00
Jan Dittberner 4ef03f141c Update issue and tag URLs for releases Sphinx plugin 2020-03-03 15:29:19 +01:00
Jan Dittberner f9ade2ea20 Release 0.6.0
- Python 3 support
 - Redis result backend
 - Automatic retry for unavailable LDAP server
 - Pipenv dependency management
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEKHuXKkUYdvdO9493DXkdyNc3wdkFAl5eZPkACgkQDXkdyNc3
 wdnpwQgAqEtqKhV2X8AmRjAUqBMO6cdMjl678cBX5d+uV8ok1SdMLZ28zFjgbCRw
 at+Du3VCVM3CoM11pgetyVO/N9pXRbhME9ogmKQY1vmHupXUvj54sUg7ia11B9OA
 cgsgTH8bKqys/ZJyV7TiKj+WinwvdUpmFdemkkcSl8Wk/MnuSI0CQt+TX79SlSET
 J1VzAedIVEP078TsSrXRBuY/AWn6UQ0ouWcjx3sP9ssLzFQ7ql3dysB1kXfrpOF9
 tldx93t7JNZVhdGK+BEy/T5CxX5LbpJCyQ5J7rt8Cd0/RK2nRPuqswuNmQfeu6mB
 t5K6rOlcWpht/waPRCAc2EJHvHMxEQ==
 =RxNE
 -----END PGP SIGNATURE-----

Merge tag '0.6.0'

Release 0.6.0

- Python 3 support
- Redis result backend
- Automatic retry for unavailable LDAP server
- Pipenv dependency management

* tag '0.6.0':
  Release preparation for 0.6.0
2020-03-03 15:09:03 +01:00
Jan Dittberner 99e22ab9dd Merge branch 'release/0.6.0' into production
* release/0.6.0: (29 commits)
  Release preparation for 0.6.0
  Fix missing bind=True
  Implement automatic retry for LDAP errors
  Unify docker setup with gvaweb and gva
  Add Dockerfile and entrypoint script
  Reorganize package structure
  Fix compatibility with Django 2.2 and other dependencies
  Use Pipenv for dependency management
  Change gvacommon dependency URL
  Update vagrant setup to stretch64 with LXC
  Protect /etc/salt/grains
  Ignore PyCharm files
  Improve Vagrant setup
  Refactoring of ldaptasks
  Mark LDAP field names as byte strings
  Change salt minion id in Vagrantfile
  Add tests for delete tasks
  Add tests for ldaptasks functions
  Update Django and gvacommon dependencies
  Add unit tests
2020-03-03 15:08:00 +01:00
Jan Dittberner 2297c82cf2 Release preparation for 0.6.0
- Bump copyright years
- Bump version number
- Update changelog
- Update documentation
2020-03-03 15:07:39 +01:00
Jan Dittberner 8875765018 Fix missing bind=True 2020-03-03 14:54:16 +01:00
Jan Dittberner 99c80f5759 Implement automatic retry for LDAP errors 2020-03-03 14:50:03 +01:00
Jan Dittberner bdcd16b7e7 Unify docker setup with gvaweb and gva 2020-03-03 13:12:41 +01:00
Jan Dittberner 628d6ee550 Add Dockerfile and entrypoint script 2020-03-03 12:22:45 +01:00
Jan Dittberner 34f788e099 Reorganize package structure
This commit reorganizes the package structure. The gvaldap.settings
modules have been merged. The gvaldap.ldaptasks module has been move up
one level to have task names without the gvaldap prefix.

isort control instructions have been added to setup.cfg.
2020-03-03 12:20:13 +01:00
Jan Dittberner 427fdd9c03 Fix compatibility with Django 2.2 and other dependencies
- drop mockldap in favour of volatildap
- fix URLs and settings
- fix tests
2020-03-02 15:40:08 +01:00
Jan Dittberner c4485945fc Use Pipenv for dependency management 2020-03-02 15:39:29 +01:00
Jan Dittberner ea07d53307 Change gvacommon dependency URL 2019-06-26 22:54:35 +02:00
Jan Dittberner 1091084438 Update vagrant setup to stretch64 with LXC 2018-08-10 11:56:29 +02:00
Jan Dittberner cab9e9701b Protect /etc/salt/grains
Make sure that the permissions of /etc/salt/grains only allow access for the
root user.
2016-09-25 17:27:42 +02:00
Jan Dittberner 2d347e6e96 Ignore PyCharm files 2016-09-25 01:10:45 +02:00
Jan Dittberner 937d0aff97 Improve Vagrant setup
Use debian/contrib-jessie64 base box, install python-cryptography before
saltstack bootstrap.
2016-09-25 01:10:17 +02:00
Jan Dittberner 2ff7dd8902 Refactoring of ldaptasks
All LDAP tasks return a dictionary now to allow chaining with other
tasks, the new task delete_ldap_user_chained has been added to allow
user deletion after other Celery tasks.
2016-02-07 22:41:36 +01:00
Jan Dittberner 54875619aa Mark LDAP field names as byte strings
The django-ldapdb code cannot handle unicode field names with real LDAP
directories. This commit marks the field names of LDAP entities as byte
strings.
2016-02-07 22:40:19 +01:00
Jan Dittberner 1a24cf9c50 Change salt minion id in Vagrantfile 2016-02-06 13:37:53 +01:00
Jan Dittberner 94aacf1398 Add tests for delete tasks
This commit raises coverage for ldaptasks.tasks to 100% by adding tests
for delete_ldap_group, delete_ldap_group_if_empty and delete_ldap_user
tasks.
2016-02-02 18:31:37 +00:00
Jan Dittberner 67ccdbb7c8 Add tests for ldaptasks functions
This commit adds tests for ldaptasks.set_ldap_user_password,
ldaptasks.add_ldap_user_to_group and
ldaptasks.remove_ldap_user_from_group.
2016-02-01 23:15:57 +00:00
Jan Dittberner 23015af083 Update Django and gvacommon dependencies
This commit updates the Django dependency version to 1.9.2 and gvacommon
to 0.3.0 that provides gvacommon.settings_utils.get_env_variable. The
gnuviechadmin.settings.base module now uses this implementation instead
of an own copy and the corresponding test has been removed too.
2016-02-01 23:37:39 +01:00
Jan Dittberner 4b060c51f4 Add unit tests
This commit adds test coverage for gvaldap and ldapentities and starts
to provide coverage for ldaptasks using mockldap.
2016-02-01 00:55:59 +00:00
Jan Dittberner 32366e93dd Add setup.cfg for QA tools, fix flake8 warnings 2016-01-31 23:30:17 +01:00
Jan Dittberner ed18c4a6f9 Modernize dependencies, sync with gva
This commit modernizes all requirements to the latest versions, adapts
the settings to Django 1.9 and synchronizes the settings layout with
gva.
2016-01-31 23:19:26 +01:00
Jan Dittberner 7c6ed136eb Switch result backend to redis
The AMQP result backend proved as impractical, this commit switches to
redis instead.
2016-01-31 21:36:40 +01:00
Jan Dittberner e9ee69f6e5 Add code quality requirements 2016-01-31 21:34:47 +01:00
Jan Dittberner 5094990c9a Switch from deprecated amqp backend to rpc
The previously used amqp result backend has been marked as deprecated by
the celery developers. This commit switches to the rpc backend that
should be used instead.

This commit renames get_env_setting to get_env_variable to be consistent
with the gva settings modules.
2016-01-31 21:34:26 +01:00
Jan Dittberner 02768a4b95 Use separate AMQP vhost for tests 2016-01-31 21:34:20 +01:00
Jan Dittberner 62d8ed64e2 Move salt states and pillar data to separate repository
This commit removes the salt states and pillar data and adds the
ldapserver role to the gvaldap host.
2016-01-31 21:32:15 +01:00
Jan Dittberner 6a8997e950 Add initial Vagrant/Saltstack setup
This commit adds an initial Vagrant and Saltstack setup that reuses the
same configuration as that of the gva repository. The LDAP server itself
is not configured yet.
2016-01-29 23:26:57 +01:00
Jan Dittberner af8b9e974c Start next development iteration
This commit moves the version number to gvaldap's __init__.py, bumps the
copyright year and starts a new development iteration. The docs/conf.py
is synchronized with the gva project's version of that file.
2016-01-29 16:57:48 +01:00
Jan Dittberner 809168d80f Merge tag '0.5.2'
release 0.5.2

- fix minor issues in log messages

* tag '0.5.2':
  update docs version, add release to changelog
2015-01-29 21:52:16 +01:00
Jan Dittberner 1079b0b1d2 Merge branch 'release/0.5.2' into production
* release/0.5.2:
  update docs version, add release to changelog
  add changelog entry
  change order of LDAP commands and logging to fix log messages
2015-01-29 21:51:50 +01:00
Jan Dittberner 261ffe5bb0 update docs version, add release to changelog 2015-01-29 21:51:25 +01:00
Jan Dittberner fad70cf74a add changelog entry 2015-01-29 21:50:23 +01:00
Jan Dittberner b0097ea0a5 change order of LDAP commands and logging to fix log messages 2015-01-29 21:35:33 +01:00
Jan Dittberner ceb1016e8f release 0.5.1
- hotfix for invalid Reject handling
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABCgAGBQJUynGoAAoJEA15HcjXN8HZ6EYIALNiQdZvjjuIMviwF4amfmb8
 K5HCOOWoxc5dr8eKWhrzL4xUg/N6MT1VcRp/zJyiwGxM3Pe0dbx/lBMkIVxtjqpG
 sQDx+QITV/eP+rGIO+/mimvfeIsghkIOPGVy+UHdsBS7H4p8d8EiSNMs9WjNzx8M
 tt5nGOONlWn5wfwha0EaaIFR51cBfmH8l/MjK+6EjyWlPmemQSMB2AmEHlGgxQPt
 Wm8Pd5vQp61WOBxGeQtSvLGeDSjBgdBB81Z7Zh9RgeJb4Yb35mmAFdF/5c3r3aEI
 MqRtXIj/uYGiOepwKKcGYShibkg4FRquGeEsame/lVNXgtqfW0PRrGclFW4XBFA=
 =31/7
 -----END PGP SIGNATURE-----

Merge tag '0.5.1'

release 0.5.1

- hotfix for invalid Reject handling

* tag '0.5.1':
  update docs version, add release to changelog
2015-01-29 18:45:17 +01:00
Jan Dittberner 94cf918bac Merge branch 'release/0.5.1' into production
* release/0.5.1:
  update docs version, add release to changelog
  reverted Reject handling in ldaptasks
2015-01-29 18:44:49 +01:00
Jan Dittberner 7e31e8d671 update docs version, add release to changelog 2015-01-29 18:44:42 +01:00
Jan Dittberner 2bca6f687f reverted Reject handling in ldaptasks 2015-01-29 18:41:44 +01:00
Jan Dittberner 5d22e63686 release 0.5.0
- better logging, updated dependencies
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABCgAGBQJUym/JAAoJEA15HcjXN8HZ9GsIANmCtRQlkuk2GB0kUnM18qu0
 /8pB9X7McSwD8JKbEi8yEu/WY4Dc17s6WELTMkt5RjNGjDDZI0vDSW3QycmnqvP1
 T/zWXT4N3PJVtIV9U2InVzoBhMBONz4E6QXvjagzTwaPtfa8F4aqBiV6yE9bAURL
 Is27Zjjy/7DArHZ8jDAG8/JIPY3ZzxBgrief46p/PhLjq7/xEdQURCHZph+UVDVA
 6hJoQ77X8KFPfCr1EEn+Tu1+1GMPu4JXh+XZOLzjab8HkORtE4/Tjbtw53hAqQV9
 +0Z6pWYPi7C7fAIaB9+E8jCiR8oMgp46Y0RefKl8kwvGlGKYM9xkAnKvbja1oO8=
 =7lQU
 -----END PGP SIGNATURE-----

Merge tag '0.5.0'

release 0.5.0

- better logging, updated dependencies

* tag '0.5.0':
  update version, add release to changelog
2015-01-29 18:37:19 +01:00
Jan Dittberner 26b890b859 Merge branch 'release/0.5.0' into production
* release/0.5.0:
  update version, add release to changelog
  improved task logging
  update dependencies
2015-01-29 18:36:27 +01:00
Jan Dittberner b7ecbfc0dc update version, add release to changelog 2015-01-29 18:35:40 +01:00
Jan Dittberner b422c554ea improved task logging 2015-01-29 18:32:13 +01:00
Jan Dittberner e4a04130e7 update dependencies
- update to Django 1.7.4
- update to bpython 0.13.2
- add explicit dependencies for Pygments and requests
- remove unused pyyaml dependency
2015-01-29 17:16:14 +01:00
Jan Dittberner 7f562cffcc release 0.4.0
-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABCgAGBQJUw7Q+AAoJEA15HcjXN8HZ+5cH/3C+DLp6eI7zvHCCHPAIY05f
 /wUnsvvvY5Is70nfce74zb/8Dt3ptyFdN1YPMCm4cRDFKXb/E+vuVi51d5LaqR6N
 i2vdDHdYThBkO5LkJQU/hW6JPYGVQ6zDF/n4RcYZ+6yQ5SSCp2VOB662RingrL0D
 rTPxA+XqtSjh43f5/zSWbc+zDlPkpay2TXnWhlrRTi+TlO/CEAdk6goJ0KbEfRQ0
 YDejM0oUg7BZ3/GYBClMOOkD6lPl7U9jN5KNbIzIbMX9x3g2p9JwYp+IFQRgL2qx
 KJl3KJDyqjpPXVYqS4lrHd4STqND8hEZtcTtOufXM9VS19HSFRoxtvCLWH14ajA=
 =B4tW
 -----END PGP SIGNATURE-----

Merge tag '0.4.0'

release 0.4.0

* tag '0.4.0':
  set version number, mark release in changelog
2015-01-24 16:03:32 +01:00
46 changed files with 1650 additions and 708 deletions

5
.gitignore vendored
View file

@ -40,3 +40,8 @@ Desktop.ini
.ropeproject
_build/
.vagrant/
.coverage
coverage-report/
.idea/
.env

52
Dockerfile Normal file
View file

@ -0,0 +1,52 @@
ARG DEBIAN_RELEASE=buster
FROM debian:$DEBIAN_RELEASE
LABEL maintainer="Jan Dittberner <jan@dittberner.info>"
ENV LC_ALL=C.UTF-8
ENV LANG=C.UTF-8
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
build-essential \
dumb-init \
gettext \
git \
python3-dev \
python3-pip \
python3-setuptools \
python3-virtualenv \
python3-wheel \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*.*
RUN python3 -m pip install --prefix=/usr/local pipenv
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
libldap2-dev \
libsasl2-dev \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*.*
ARG GVAGID=2000
ARG GVAUID=2000
ARG GVAAPP=gvaldap
WORKDIR /srv/$GVAAPP
COPY Pipfile Pipfile.lock /srv/$GVAAPP/
RUN addgroup --gid $GVAGID $GVAAPP ; \
adduser --home /home/$GVAAPP --shell /bin/bash --uid $GVAUID --gid $GVAGID --disabled-password --gecos "User for gnuviechadmin component $GVAAPP" $GVAAPP
USER $GVAAPP
RUN python3 -m virtualenv --python=python3 /home/$GVAAPP/$GVAAPP-venv ; \
/home/$GVAAPP/$GVAAPP-venv/bin/python3 -m pip install -U pip ; \
VIRTUAL_ENV=/home/$GVAAPP/$GVAAPP-venv pipenv install --deploy --ignore-pipfile --dev
VOLUME /srv/$GVAAPP
COPY gvaldap.sh /srv/
ENTRYPOINT ["dumb-init", "/srv/gvaldap.sh"]

View file

@ -1,4 +1,4 @@
Copyright (c) 2014, 2015 Jan Dittberner
Copyright (c) 2014-2020 Jan Dittberner
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation

33
Pipfile Normal file
View file

@ -0,0 +1,33 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[[source]]
url = "https://$PYPI_GNUVIECH_USERNAME:${PYPI_GNUVIECH_PASSWORD}@pypi.gnuviech-server.de/simple"
name = "gnuviech"
verify_ssl = true
[dev-packages]
Sphinx = "*"
coverage = "*"
django-debug-toolbar = "*"
releases = "==1.0.0"
volatildap = "*"
[packages]
gvacommon = {version = "*",index = "gnuviech"}
Django = "<3"
amqp = "*"
celery = "*"
django-braces = "*"
django-ldapdb = "*"
django-model-utils = "*"
kombu = "*"
passlib = "*"
python-ldap = "*"
pytz = "*"
redis = "*"
[requires]
python_version = "3.7"

462
Pipfile.lock generated Normal file
View file

@ -0,0 +1,462 @@
{
"_meta": {
"hash": {
"sha256": "447a8091237b0e5a54b560066ec2329cce2fc5d564bbc2b37e2f9ec0c1af8ea2"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.7"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
},
{
"name": "gnuviech",
"url": "https://$PYPI_GNUVIECH_USERNAME:${PYPI_GNUVIECH_PASSWORD}@pypi.gnuviech-server.de/simple",
"verify_ssl": true
}
]
},
"default": {
"amqp": {
"hashes": [
"sha256:6e649ca13a7df3faacdc8bbb280aa9a6602d22fd9d545336077e573a1f4ff3b8",
"sha256:77f1aef9410698d20eaeac5b73a87817365f457a507d82edf292e12cbb83b08d"
],
"index": "pypi",
"version": "==2.5.2"
},
"billiard": {
"hashes": [
"sha256:bff575450859a6e0fbc2f9877d9b715b0bbc07c3565bb7ed2280526a0cdf5ede",
"sha256:d91725ce6425f33a97dfa72fb6bfef0e47d4652acd98a032bd1a7fbf06d5fa6a"
],
"version": "==3.6.3.0"
},
"celery": {
"hashes": [
"sha256:3c5fcd6bfcf9a6323cb742cfc121d1790d50cfeddf300ba723cfa0b356413f07",
"sha256:a650525303ee866fb0c62c82f68681fcc2183eebbfafae552c27d30125fe518b"
],
"index": "pypi",
"version": "==4.4.1"
},
"django": {
"hashes": [
"sha256:1226168be1b1c7efd0e66ee79b0e0b58b2caa7ed87717909cd8a57bb13a7079a",
"sha256:9a4635813e2d498a3c01b10c701fe4a515d76dd290aaa792ccb65ca4ccb6b038"
],
"index": "pypi",
"version": "==2.2.10"
},
"django-braces": {
"hashes": [
"sha256:83705b78948de00804bfacf40c315d001bb39630f35bbdd8588211c2d5b4d43f",
"sha256:a6d9b34cf3e4949635e54884097c30410d7964fc7bec7231445ea7079b8c5722"
],
"index": "pypi",
"version": "==1.14.0"
},
"django-ldapdb": {
"hashes": [
"sha256:36990757f26c1bd7642bbb0ed88cc1a4d8fe945dfcae6094142b9889b976e3f8",
"sha256:6c2d3b645fab20f97f1d33d8924114b85f699fca1bdf2cdd251074fa0331d75e"
],
"index": "pypi",
"version": "==1.4.0"
},
"django-model-utils": {
"hashes": [
"sha256:9cf882e5b604421b62dbe57ad2b18464dc9c8f963fc3f9831badccae66c1139c",
"sha256:adf09e5be15122a7f4e372cb5a6dd512bbf8d78a23a90770ad0983ee9d909061"
],
"index": "pypi",
"version": "==4.0.0"
},
"gvacommon": {
"hashes": [
"sha256:adf1ebc824433196d112764c61d9ca869481d33f612818c2840069f57ab42c25"
],
"index": "gnuviech",
"version": "==0.5.0"
},
"importlib-metadata": {
"hashes": [
"sha256:06f5b3a99029c7134207dd882428a66992a9de2bef7c2b699b5641f9886c3302",
"sha256:b97607a1a18a5100839aec1dc26a1ea17ee0d93b20b0f008d80a5a050afb200b"
],
"markers": "python_version < '3.8'",
"version": "==1.5.0"
},
"kombu": {
"hashes": [
"sha256:2d1cda774126a044d91a7ff5fa6d09edf99f46924ab332a810760fe6740e9b76",
"sha256:598e7e749d6ab54f646b74b2d2df67755dee13894f73ab02a2a9feb8870c7cb2"
],
"index": "pypi",
"version": "==4.6.8"
},
"passlib": {
"hashes": [
"sha256:68c35c98a7968850e17f1b6892720764cc7eed0ef2b7cb3116a89a28e43fe177",
"sha256:8d666cef936198bc2ab47ee9b0410c94adf2ba798e5a84bf220be079ae7ab6a8"
],
"index": "pypi",
"version": "==1.7.2"
},
"pyasn1": {
"hashes": [
"sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d",
"sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"
],
"version": "==0.4.8"
},
"pyasn1-modules": {
"hashes": [
"sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e",
"sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74"
],
"version": "==0.2.8"
},
"python-ldap": {
"hashes": [
"sha256:7d1c4b15375a533564aad3d3deade789221e450052b21ebb9720fb822eccdb8e"
],
"index": "pypi",
"version": "==3.2.0"
},
"pytz": {
"hashes": [
"sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d",
"sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"
],
"index": "pypi",
"version": "==2019.3"
},
"redis": {
"hashes": [
"sha256:0dcfb335921b88a850d461dc255ff4708294943322bd55de6cfd68972490ca1f",
"sha256:b205cffd05ebfd0a468db74f0eedbff8df1a7bfc47521516ade4692991bb0833"
],
"index": "pypi",
"version": "==3.4.1"
},
"six": {
"hashes": [
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
"sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
],
"version": "==1.14.0"
},
"sqlparse": {
"hashes": [
"sha256:022fb9c87b524d1f7862b3037e541f68597a730a8843245c349fc93e1643dc4e",
"sha256:e162203737712307dfe78860cc56c8da8a852ab2ee33750e33aeadf38d12c548"
],
"version": "==0.3.1"
},
"vine": {
"hashes": [
"sha256:133ee6d7a9016f177ddeaf191c1f58421a1dcc6ee9a42c58b34bed40e1d2cd87",
"sha256:ea4947cc56d1fd6f2095c8d543ee25dad966f78692528e68b4fada11ba3f98af"
],
"version": "==1.3.0"
},
"zipp": {
"hashes": [
"sha256:12248a63bbdf7548f89cb4c7cda4681e537031eda29c02ea29674bc6854460c2",
"sha256:7c0f8e91abc0dc07a5068f315c52cb30c66bfbc581e5b50704c8a2f6ebae794a"
],
"version": "==3.0.0"
}
},
"develop": {
"alabaster": {
"hashes": [
"sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359",
"sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"
],
"version": "==0.7.12"
},
"asgiref": {
"hashes": [
"sha256:7e06d934a7718bf3975acbf87780ba678957b87c7adc056f13b6215d610695a0",
"sha256:ea448f92fc35a0ef4b1508f53a04c4670255a3f33d22a81c8fc9c872036adbe5"
],
"version": "==3.2.3"
},
"babel": {
"hashes": [
"sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38",
"sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4"
],
"version": "==2.8.0"
},
"certifi": {
"hashes": [
"sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3",
"sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"
],
"version": "==2019.11.28"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"version": "==3.0.4"
},
"coverage": {
"hashes": [
"sha256:15cf13a6896048d6d947bf7d222f36e4809ab926894beb748fc9caa14605d9c3",
"sha256:1daa3eceed220f9fdb80d5ff950dd95112cd27f70d004c7918ca6dfc6c47054c",
"sha256:1e44a022500d944d42f94df76727ba3fc0a5c0b672c358b61067abb88caee7a0",
"sha256:25dbf1110d70bab68a74b4b9d74f30e99b177cde3388e07cc7272f2168bd1477",
"sha256:3230d1003eec018ad4a472d254991e34241e0bbd513e97a29727c7c2f637bd2a",
"sha256:3dbb72eaeea5763676a1a1efd9b427a048c97c39ed92e13336e726117d0b72bf",
"sha256:5012d3b8d5a500834783689a5d2292fe06ec75dc86ee1ccdad04b6f5bf231691",
"sha256:51bc7710b13a2ae0c726f69756cf7ffd4362f4ac36546e243136187cfcc8aa73",
"sha256:527b4f316e6bf7755082a783726da20671a0cc388b786a64417780b90565b987",
"sha256:722e4557c8039aad9592c6a4213db75da08c2cd9945320220634f637251c3894",
"sha256:76e2057e8ffba5472fd28a3a010431fd9e928885ff480cb278877c6e9943cc2e",
"sha256:77afca04240c40450c331fa796b3eab6f1e15c5ecf8bf2b8bee9706cd5452fef",
"sha256:7afad9835e7a651d3551eab18cbc0fdb888f0a6136169fbef0662d9cdc9987cf",
"sha256:9bea19ac2f08672636350f203db89382121c9c2ade85d945953ef3c8cf9d2a68",
"sha256:a8b8ac7876bc3598e43e2603f772d2353d9931709345ad6c1149009fd1bc81b8",
"sha256:b0840b45187699affd4c6588286d429cd79a99d509fe3de0f209594669bb0954",
"sha256:b26aaf69713e5674efbde4d728fb7124e429c9466aeaf5f4a7e9e699b12c9fe2",
"sha256:b63dd43f455ba878e5e9f80ba4f748c0a2156dde6e0e6e690310e24d6e8caf40",
"sha256:be18f4ae5a9e46edae3f329de2191747966a34a3d93046dbdf897319923923bc",
"sha256:c312e57847db2526bc92b9bfa78266bfbaabac3fdcd751df4d062cd4c23e46dc",
"sha256:c60097190fe9dc2b329a0eb03393e2e0829156a589bd732e70794c0dd804258e",
"sha256:c62a2143e1313944bf4a5ab34fd3b4be15367a02e9478b0ce800cb510e3bbb9d",
"sha256:cc1109f54a14d940b8512ee9f1c3975c181bbb200306c6d8b87d93376538782f",
"sha256:cd60f507c125ac0ad83f05803063bed27e50fa903b9c2cfee3f8a6867ca600fc",
"sha256:d513cc3db248e566e07a0da99c230aca3556d9b09ed02f420664e2da97eac301",
"sha256:d649dc0bcace6fcdb446ae02b98798a856593b19b637c1b9af8edadf2b150bea",
"sha256:d7008a6796095a79544f4da1ee49418901961c97ca9e9d44904205ff7d6aa8cb",
"sha256:da93027835164b8223e8e5af2cf902a4c80ed93cb0909417234f4a9df3bcd9af",
"sha256:e69215621707119c6baf99bda014a45b999d37602cb7043d943c76a59b05bf52",
"sha256:ea9525e0fef2de9208250d6c5aeeee0138921057cd67fcef90fbed49c4d62d37",
"sha256:fca1669d464f0c9831fd10be2eef6b86f5ebd76c724d1e0706ebdff86bb4adf0"
],
"index": "pypi",
"version": "==5.0.3"
},
"django": {
"hashes": [
"sha256:1226168be1b1c7efd0e66ee79b0e0b58b2caa7ed87717909cd8a57bb13a7079a",
"sha256:9a4635813e2d498a3c01b10c701fe4a515d76dd290aaa792ccb65ca4ccb6b038"
],
"index": "pypi",
"version": "==2.2.10"
},
"django-debug-toolbar": {
"hashes": [
"sha256:eabbefe89881bbe4ca7c980ff102e3c35c8e8ad6eb725041f538988f2f39a943",
"sha256:ff94725e7aae74b133d0599b9bf89bd4eb8f5d2c964106e61d11750228c8774c"
],
"index": "pypi",
"version": "==2.2"
},
"docutils": {
"hashes": [
"sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af",
"sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"
],
"version": "==0.16"
},
"idna": {
"hashes": [
"sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
"sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
],
"version": "==2.9"
},
"imagesize": {
"hashes": [
"sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1",
"sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"
],
"version": "==1.2.0"
},
"jinja2": {
"hashes": [
"sha256:93187ffbc7808079673ef52771baa950426fd664d3aad1d0fa3e95644360e250",
"sha256:b0eaf100007721b5c16c1fc1eecb87409464edc10469ddc9a22a27a99123be49"
],
"version": "==2.11.1"
},
"markupsafe": {
"hashes": [
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
"sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42",
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
"sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b",
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
"sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15",
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
"sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2",
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7",
"sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"
],
"version": "==1.1.1"
},
"packaging": {
"hashes": [
"sha256:170748228214b70b672c581a3dd610ee51f733018650740e98c7df862a583f73",
"sha256:e665345f9eef0c621aa0bf2f8d78cf6d21904eef16a93f020240b704a57f1334"
],
"version": "==20.1"
},
"pygments": {
"hashes": [
"sha256:2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b",
"sha256:98c8aa5a9f778fcd1026a17361ddaf7330d1b7c62ae97c3bb0ae73e0b9b6b0fe"
],
"version": "==2.5.2"
},
"pyparsing": {
"hashes": [
"sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f",
"sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec"
],
"version": "==2.4.6"
},
"pytz": {
"hashes": [
"sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d",
"sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"
],
"index": "pypi",
"version": "==2019.3"
},
"releases": {
"hashes": [
"sha256:23792ba473dca124e1b60f3bb6428d775b84b05ee73c0bfd1ba8439b833c5749",
"sha256:a9e65295578bf2e352ccc86d33c12b6b7ba27e3da1342d2ffee7f3f7d602a604"
],
"index": "pypi",
"version": "==1.0.0"
},
"requests": {
"hashes": [
"sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee",
"sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"
],
"version": "==2.23.0"
},
"six": {
"hashes": [
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
"sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
],
"version": "==1.14.0"
},
"snowballstemmer": {
"hashes": [
"sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0",
"sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"
],
"version": "==2.0.0"
},
"sphinx": {
"hashes": [
"sha256:776ff8333181138fae52df65be733127539623bb46cc692e7fa0fcfc80d7aa88",
"sha256:ca762da97c3b5107cbf0ab9e11d3ec7ab8d3c31377266fd613b962ed971df709"
],
"index": "pypi",
"version": "==2.4.3"
},
"sphinxcontrib-applehelp": {
"hashes": [
"sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a",
"sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"
],
"version": "==1.0.2"
},
"sphinxcontrib-devhelp": {
"hashes": [
"sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e",
"sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"
],
"version": "==1.0.2"
},
"sphinxcontrib-htmlhelp": {
"hashes": [
"sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f",
"sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b"
],
"version": "==1.0.3"
},
"sphinxcontrib-jsmath": {
"hashes": [
"sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178",
"sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"
],
"version": "==1.0.1"
},
"sphinxcontrib-qthelp": {
"hashes": [
"sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72",
"sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"
],
"version": "==1.0.3"
},
"sphinxcontrib-serializinghtml": {
"hashes": [
"sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc",
"sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a"
],
"version": "==1.1.4"
},
"sqlparse": {
"hashes": [
"sha256:022fb9c87b524d1f7862b3037e541f68597a730a8843245c349fc93e1643dc4e",
"sha256:e162203737712307dfe78860cc56c8da8a852ab2ee33750e33aeadf38d12c548"
],
"version": "==0.3.1"
},
"urllib3": {
"hashes": [
"sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
"sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
],
"version": "==1.25.8"
},
"volatildap": {
"hashes": [
"sha256:7ef4cac02c4f907b87d5e3f5e872287a874d0c0d0a7c1054a8caae8e9c17b367",
"sha256:d9ef3ee7fcdf8d3ae1dd243e0a8c3892e404e4552caa60153fa0de97287bcfc6"
],
"index": "pypi",
"version": "==1.3.0"
}
}
}

View file

@ -10,4 +10,4 @@ customer management at `Jan Dittberner IT-Consulting & -Solutions
Read the :doc:`Installation instructions <install>` to get started locally.
The project page for gvaldap is at http://dev.gnuviech-server.de/gvaldap.
The project page for gvaldap is at http://git.dittberner.info/gnuviech/gvaldap.

31
Vagrantfile vendored Normal file
View file

@ -0,0 +1,31 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure(2) do |config|
config.vm.box = "debian/buster64"
config.vm.hostname = "gvaldap.local"
config.vm.network :private_network, :ip => "172.16.3.3"
config.vm.network "forwarded_port", guest: 8000, host: 8001
config.vm.synced_folder "../gvasalt/states/", "/srv/salt/"
config.vm.synced_folder "../gvasalt/pillar/", "/srv/pillar/"
config.vm.provider :libvirt do |libvirt|
libvirt.memory = 1024
end
config.vm.provision :shell, path: "change-vmdebootstrap-default-dhcp.sh"
config.vm.provision :salt do |salt|
salt.bootstrap_options = "-x python3"
salt.minion_id = "gvaldap"
salt.masterless = true
salt.run_highstate = true
salt.verbose = true
salt.colorize = true
salt.log_level = "warning"
salt.grains_config = "salt/grains"
end
end

View file

@ -0,0 +1,15 @@
#!/bin/sh
set -e
debootstrap_network=/etc/systemd/network/99-dhcp.network
if grep -q '^Name=\\*' "${debootstrap_network}"; then
primary_nic=$(ls -1 /sys/class/net | grep -v lo |sort | head -1)
sed -i "s/^Name=e\\*/Name=${primary_nic}/" \
"${debootstrap_network}"
systemctl restart systemd-networkd.service
echo "Changed systemd network configuration"
else
echo "Systemd network configuration has already been changed"
fi

View file

@ -1,6 +1,30 @@
Changelog
=========
* :release:`0.7.0 <2020-04-06>`
* :support:`-` update Vagrant setup to Debian Buster and Python 3
* :release:`0.6.0 <2020-03-03>`
* :support:`-` add Python 3 support
* :support:`-` upgrade to Django 2.2.10
* :support:`-` use Pipenv for dependency management
* :feature:`-` properly handle unavailable LDAP server
* :release:`0.5.2 <2015-01-29>`
* :bug:`-` fix minor log message issue
* :release:`0.5.1 <2015-01-29>`
* :bug:`-` reverted Reject handling in ldaptasks
* :release:`0.5.0 <2015-01-29>`
* :feature:`-` improved logging of ldaptasks
* :support:`-` update bpython to 0.13.2, add explicit dependency on requests
2.5.1
* :support:`-` add explicit dependency for Pygments used by Sphinxdoc
* :support:`-` remove pyyaml dependency as json is used for message
serialization
* :support:`-` update to Django version 1.7.4
* :release:`0.4.0 <2015-01-24>`
* :feature:`-` add new task :py:func:`ldaptasks.tasks.set_ldap_user_password`

View file

@ -16,13 +16,6 @@ The project module :py:mod:`gvaldap`
.. automodule:: gvaldap
:py:mod:`celery <gvaldap.celery>`
---------------------------------
.. automodule:: gvaldap.celery
:members:
:py:mod:`urls <gvaldap.urls>`
-----------------------------
@ -41,27 +34,6 @@ The project module :py:mod:`gvaldap`
.. automodule:: gvaldap.settings
:py:mod:`base <gvaldap.settings.base>`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: gvaldap.settings.base
:members:
:py:mod:`local <gvaldap.settings.local>`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: gvaldap.settings.local
:py:mod:`production <gvaldap.settings.production>`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: gvaldap.settings.production
:py:mod:`test <gvaldap.settings.test>`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: gvaldap.settings.test
:py:mod:`ldapentities` app
==========================
@ -89,6 +61,13 @@ The project module :py:mod:`gvaldap`
.. automodule:: ldaptasks
:py:mod:`celery <ldaptasks.celery>`
-----------------------------------
.. automodule:: ldaptasks.celery
:members:
:py:mod:`tasks <ldaptasks.tasks>`
---------------------------------

View file

@ -21,6 +21,7 @@ import django
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath(os.path.join('..', 'gvaldap')))
os.environ['DJANGO_SETTINGS_MODULE'] = 'gvaldap.settings'
os.environ['GVALDAP_ALLOWED_HOSTS'] = 'localhost'
os.environ['GVALDAP_SERVER_EMAIL'] = 'root@localhost'
@ -35,13 +36,13 @@ django.setup()
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['releases', 'sphinx.ext.autodoc', 'celery.contrib.sphinx']
# configuration for releases extension
releases_issue_uri = 'https://git.dittberner.info/gnuviech/gvaldap/issues/%s'
releases_release_uri = 'https://git.dittberner.info/gnuviech/gvaldap/src/tag/%s'
# Add any paths that contain templates here, relative to this directory.
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.
source_suffix = '.rst'
@ -53,16 +54,17 @@ master_doc = 'index'
# General information about the project.
project = u'gvaldap'
copyright = u'2014, 2015 Jan Dittberner'
copyright = u'2014-2020, Jan Dittberner'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.4'
# The full version, including alpha/beta/rc tags.
release = '0.4.0'
from gvaldap import __version__ as release
# The short X.Y version.
version = ".".join(release.split('.')[:2])
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@ -103,7 +105,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
html_theme = 'alabaster'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the

View file

@ -7,4 +7,4 @@ 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
* setup of celery worker under control of systemd

View file

@ -7,65 +7,17 @@ Install
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.
To get a running work environment use `pipenv`_.
.. index:: virtualenv
.. _pipenv: https://pipenv.kennethreitz.org/en/latest/
Virtualenv Only
---------------
First, make sure you are using `virtualenv`_. Once that's installed, create
your virtualenv:
To get started install `pip` and `pipenv` and use `pipenv install --dev`:
.. 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
$ apt install python3-pip
$ python3 -m pip install --user -U pipenv
$ pipenv install --dev
.. index:: celery, worker, ldap queue
@ -79,6 +31,6 @@ into the gvaldap directory and run the celery worker with:
.. code-block:: sh
$ cd gvaldap
$ celery -A gvaldap worker -Q ldap -l info
$ pipenv run celery -A ldaptasks worker -Q web -l info
.. _Celery: http://www.celeryproject.org/

7
gvaldap.sh Executable file
View file

@ -0,0 +1,7 @@
#!/bin/sh
set -e
. /home/gvaldap/gvaldap-venv/bin/activate
cd /srv/gvaldap/gvaldap
celery -A ldaptasks worker -Q ldap -l info

View file

@ -1,3 +0,0 @@
.*.swp
*.pyc
.ropeproject/

View file

@ -1,15 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
class GvaRouter(object):
def route_for_task(self, task, args=None, kwargs=None):
for route in ['ldap', 'file', 'mysql', 'pgsql']:
if route in task:
return {
'exchange': route,
'exchange_type': 'direct',
'queue': route,
}
return None

View file

@ -1,3 +1,8 @@
"""
This is the gvaldap project module.
"""
__version__ = "0.7.0"
from ldaptasks.celery import app as celery_app
__all__ = ("celery_app",)

View file

@ -1,23 +0,0 @@
"""
This module defines the Celery_ app for gvaldap.
.. _Celery: http://www.celeryproject.org/
"""
from __future__ import absolute_import
import os
from celery import Celery
from django.conf import settings
os.environ.setdefault('DJANGO_SETTINGS_MODULE',
'gvaldap.settings.production')
#: The Celery application
app = Celery('gvaldap')
app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

315
gvaldap/gvaldap/settings.py Normal file
View file

@ -0,0 +1,315 @@
# -*- coding: utf-8 -*-
# pymode:lint_ignore=E501
"""
Common settings and globals.
"""
from os.path import abspath, basename, dirname, join, normpath
from sys import path
from gvacommon.settings_utils import get_env_variable
# ######### PATH CONFIGURATION
# Absolute filesystem path to the Django project directory:
DJANGO_ROOT = dirname(dirname(abspath(__file__)))
# Absolute filesystem path to the top-level project folder:
SITE_ROOT = dirname(DJANGO_ROOT)
# Site name:
SITE_NAME = basename(DJANGO_ROOT)
# Add our project to our pythonpath, this way we don't need to type our project
# name in our dotted import paths:
path.append(DJANGO_ROOT)
# ######### END PATH CONFIGURATION
# ######### DEBUG CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#debug
DEBUG = get_env_variable("GVALDAP_DEBUG", bool, False)
# ######### MANAGER CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#admins
ADMINS = (
(get_env_variable("GVALDAP_ADMIN_NAME"), get_env_variable("GVALDAP_ADMIN_EMAIL")),
)
# See: https://docs.djangoproject.com/en/dev/ref/settings/#managers
MANAGERS = ADMINS
# ######### END MANAGER CONFIGURATION
# ######### DATABASE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": normpath(join(DJANGO_ROOT, "default.db")),
"USER": "",
"PASSWORD": "",
"HOST": "",
"PORT": "",
},
"ldap": {
"ENGINE": "ldapdb.backends.ldap",
"NAME": get_env_variable("GVALDAP_LDAP_URL"),
"USER": get_env_variable("GVALDAP_LDAP_USER"),
"PASSWORD": get_env_variable("GVALDAP_LDAP_PASSWORD"),
},
}
DATABASE_ROUTERS = ["ldapdb.router.Router"]
# ######### END DATABASE CONFIGURATION
# ######### GENERAL CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#time-zone
TIME_ZONE = "Europe/Berlin"
# See: https://docs.djangoproject.com/en/dev/ref/settings/#language-code
LANGUAGE_CODE = "en-us"
# See: https://docs.djangoproject.com/en/dev/ref/settings/#site-id
SITE_ID = 1
# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-i18n
USE_I18N = True
# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-l10n
USE_L10N = True
# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-tz
USE_TZ = True
# ######### END GENERAL CONFIGURATION
# ######### MEDIA CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#media-root
MEDIA_ROOT = normpath(join(SITE_ROOT, "media"))
# See: https://docs.djangoproject.com/en/dev/ref/settings/#media-url
MEDIA_URL = "/media/"
# ######### END MEDIA CONFIGURATION
# ######### STATIC FILE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url
STATIC_URL = "/static/"
# See:
# https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders # noqa
STATICFILES_FINDERS = ("django.contrib.staticfiles.finders.AppDirectoriesFinder",)
# ######### END STATIC FILE CONFIGURATION
# ######### SECRET CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
# Note: This key should only be used for development and testing.
SECRET_KEY = get_env_variable("GVALDAP_SECRETKEY")
# ######### END SECRET CONFIGURATION
# ######### FIXTURE CONFIGURATION
# See:
# https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FIXTURE_DIRS # noqa
FIXTURE_DIRS = (normpath(join(SITE_ROOT, "fixtures")),)
# ######### END FIXTURE CONFIGURATION
# ######### TEMPLATE CONFIGURATION
# See: https://docs.djangoproject.com/en/1.9/ref/settings/#std:setting-TEMPLATES # noqa
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [normpath(join(SITE_ROOT, "templates"))],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.contrib.auth.context_processors.auth",
"django.template.context_processors.debug",
"django.template.context_processors.i18n",
"django.template.context_processors.media",
"django.template.context_processors.static",
"django.template.context_processors.tz",
"django.contrib.messages.context_processors.messages",
"django.template.context_processors.request",
]
},
}
]
# ######### END TEMPLATE CONFIGURATION
# ######### MIDDLEWARE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#middleware-classes
MIDDLEWARE = (
# Default Django middleware.
"django.middleware.common.CommonMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.locale.LocaleMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
)
# ######### END MIDDLEWARE CONFIGURATION
# ######### URL CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf
ROOT_URLCONF = "%s.urls" % SITE_NAME
# ######### END URL CONFIGURATION
# ######### TEST RUNNER CONFIGURATION
TEST_RUNNER = "django.test.runner.DiscoverRunner"
# ######### END TEST RUNNER CONFIGURATION
# ######### APP CONFIGURATION
DJANGO_APPS = (
# Default Django apps:
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.sites",
"django.contrib.messages",
"django.contrib.staticfiles",
# Admin panel and documentation:
"django.contrib.admin",
)
# Apps specific for this project go here.
LOCAL_APPS = ("ldapentities", "ldaptasks")
# See: https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
INSTALLED_APPS = DJANGO_APPS + LOCAL_APPS
# ######### END APP CONFIGURATION
# ######### LOGGING CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#logging
# A sample logging configuration. The only tangible logging
# performed by this configuration is to send an email to
# the site admins on every HTTP 500 error when DEBUG=False.
# See http://docs.djangoproject.com/en/dev/topics/logging for
# more details on how to customize your logging configuration.
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"verbose": {
"format": "%(levelname)s %(asctime)s %(name)s "
"%(module)s:%(lineno)d %(process)d %(thread)d %(message)s"
},
"simple": {"format": "%(levelname)s %(name)s:%(lineno)d %(message)s"},
},
"filters": {"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}},
"handlers": {
"mail_admins": {
"level": "ERROR",
"filters": ["require_debug_false"],
"class": "django.utils.log.AdminEmailHandler",
}
},
"loggers": {
"django.request": {
"handlers": ["mail_admins"],
"level": "ERROR",
"propagate": True,
}
},
}
# ######### END LOGGING CONFIGURATION
# ######### WSGI CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#wsgi-application
WSGI_APPLICATION = "%s.wsgi.application" % SITE_NAME
# ######### END WSGI CONFIGURATION
# ######### LDAP SETTINGS
GROUP_BASE_DN = get_env_variable("GVALDAP_BASEDN_GROUP")
USER_BASE_DN = get_env_variable("GVALDAP_BASEDN_USER")
# ######### END LDAP SETTINGS
# ######### CELERY CONFIGURATION
CELERY_BROKER_URL = get_env_variable("GVALDAP_BROKER_URL")
CELERY_RESULT_BACKEND = get_env_variable("GVALDAP_RESULTS_REDIS_URL")
CELERY_TASK_RESULT_EXPIRES = None
CELERY_ROUTES = ("gvacommon.celeryrouters.GvaRouter",)
CELERY_TIMEZONE = "Europe/Berlin"
CELERY_ENABLE_UTC = True
CELERY_ACCEPT_CONTENT = ["json"]
CELERY_TASK_SERIALIZER = "json"
CELERY_RESULT_SERIALIZER = "json"
# ######### END CELERY CONFIGURATION
if DEBUG:
TEMPLATES[0]["OPTIONS"]["debug"] = DEBUG
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
CACHES = {"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}}
INSTALLED_APPS += ("debug_toolbar",)
MIDDLEWARE += ("debug_toolbar.middleware.DebugToolbarMiddleware",)
LOGGING["handlers"].update(
{
"console": {
"level": "DEBUG",
"class": "logging.StreamHandler",
"formatter": "simple",
}
}
)
LOGGING["loggers"].update(
dict(
[
(key, {"handlers": ["console"], "level": "DEBUG", "propagate": True})
for key in ["ldapentities", "ldaptasks"]
]
)
)
INTERNAL_IPS = get_env_variable("GVALDAP_INTERNAL_IPS", str, "127.0.0.1").split(",")
else:
ALLOWED_HOSTS = get_env_variable("GVALDAP_ALLOWED_HOSTS").split(",")
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_SUBJECT_PREFIX = "[%s] " % SITE_NAME
DEFAULT_FROM_EMAIL = get_env_variable("GVALDAP_ADMIN_EMAIL")
SERVER_EMAIL = get_env_variable("GVALDAP_SERVER_EMAIL")
if get_env_variable("GVALDAP_TEST", bool, False):
PASSWORD_HASHERS = ("django.contrib.auth.hashers.MD5PasswordHasher",)
# ######### IN-MEMORY TEST DATABASE
DATABASES["default"] = {
"ENGINE": "django.db.backends.sqlite3",
"NAME": ":memory:",
"USER": "",
"PASSWORD": "",
"HOST": "",
"PORT": "",
}
LOGGING["handlers"].update(
{
"console": {
"level": "ERROR",
"class": "logging.StreamHandler",
"formatter": "simple",
}
}
)
LOGGING["loggers"].update(
dict(
[
(key, {"handlers": ["console"], "level": "ERROR", "propagate": True})
for key in ["ldapentities", "ldaptasks"]
]
)
)
CELERY_BROKER_URL = CELERY_BROKER_URL + "_test"
CELERY_RESULT_PERSISTENT = False

View file

@ -1,3 +0,0 @@
"""
This module contains settings for various environments.
"""

View file

@ -1,301 +0,0 @@
# -*- coding: utf-8 -*-
# pymode:lint_ignore=E501
"""
Common settings and globals.
"""
from os.path import abspath, basename, dirname, join, normpath
from sys import path
from os import environ
# Normally you should not import ANYTHING from Django directly
# into your settings, but ImproperlyConfigured is an exception.
from django.core.exceptions import ImproperlyConfigured
def get_env_setting(setting):
"""
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:
return environ[setting]
except KeyError:
error_msg = "Set the %s env variable" % setting
raise ImproperlyConfigured(error_msg)
########## PATH CONFIGURATION
# Absolute filesystem path to the Django project directory:
DJANGO_ROOT = dirname(dirname(abspath(__file__)))
# Absolute filesystem path to the top-level project folder:
SITE_ROOT = dirname(DJANGO_ROOT)
# Site name:
SITE_NAME = basename(DJANGO_ROOT)
# Add our project to our pythonpath, this way we don't need to type our project
# name in our dotted import paths:
path.append(DJANGO_ROOT)
########## END PATH CONFIGURATION
########## DEBUG CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#debug
DEBUG = False
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-debug
TEMPLATE_DEBUG = DEBUG
########## END DEBUG CONFIGURATION
########## MANAGER CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#admins
ADMINS = (
(get_env_setting('GVALDAP_ADMIN_NAME'), get_env_setting('GVALDAP_ADMIN_EMAIL')),
)
# See: https://docs.djangoproject.com/en/dev/ref/settings/#managers
MANAGERS = ADMINS
########## END MANAGER CONFIGURATION
########## DATABASE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': normpath(join(DJANGO_ROOT, 'default.db')),
'USER': '',
'PASSWORD': '',
'HOST': '',
'PORT': '',
},
'ldap': {
'ENGINE': 'ldapdb.backends.ldap',
'NAME': get_env_setting('GVALDAP_LDAP_URL'),
'USER': get_env_setting('GVALDAP_LDAP_USER'),
'PASSWORD': get_env_setting('GVALDAP_LDAP_PASSWORD'),
}
}
DATABASE_ROUTERS = ['ldapdb.router.Router']
########## END DATABASE CONFIGURATION
########## GENERAL CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#time-zone
TIME_ZONE = 'Europe/Berlin'
# See: https://docs.djangoproject.com/en/dev/ref/settings/#language-code
LANGUAGE_CODE = 'en-us'
# See: https://docs.djangoproject.com/en/dev/ref/settings/#site-id
SITE_ID = 1
# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-i18n
USE_I18N = True
# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-l10n
USE_L10N = True
# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-tz
USE_TZ = True
########## END GENERAL CONFIGURATION
########## MEDIA CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#media-root
MEDIA_ROOT = normpath(join(SITE_ROOT, 'media'))
# See: https://docs.djangoproject.com/en/dev/ref/settings/#media-url
MEDIA_URL = '/media/'
########## END MEDIA CONFIGURATION
########## STATIC FILE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#static-root
STATIC_ROOT = normpath(join(SITE_ROOT, 'assets'))
# See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url
STATIC_URL = '/static/'
# See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS
STATICFILES_DIRS = (
normpath(join(SITE_ROOT, 'static')),
)
# See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
)
########## END STATIC FILE CONFIGURATION
########## SECRET CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
# Note: This key should only be used for development and testing.
SECRET_KEY = get_env_setting('GVALDAP_SECRETKEY')
########## END SECRET CONFIGURATION
########## SITE CONFIGURATION
# Hosts/domain names that are valid for this site
# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
ALLOWED_HOSTS = []
########## END SITE CONFIGURATION
########## FIXTURE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FIXTURE_DIRS
FIXTURE_DIRS = (
normpath(join(SITE_ROOT, 'fixtures')),
)
########## END FIXTURE CONFIGURATION
########## TEMPLATE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors
TEMPLATE_CONTEXT_PROCESSORS = (
'django.contrib.auth.context_processors.auth',
'django.core.context_processors.debug',
'django.core.context_processors.i18n',
'django.core.context_processors.media',
'django.core.context_processors.static',
'django.core.context_processors.tz',
'django.contrib.messages.context_processors.messages',
'django.core.context_processors.request',
)
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-loaders
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
)
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-dirs
TEMPLATE_DIRS = (
normpath(join(SITE_ROOT, 'templates')),
)
########## END TEMPLATE CONFIGURATION
########## MIDDLEWARE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#middleware-classes
MIDDLEWARE_CLASSES = (
# Default Django middleware.
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
########## END MIDDLEWARE CONFIGURATION
########## URL CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf
ROOT_URLCONF = '%s.urls' % SITE_NAME
########## END URL CONFIGURATION
########## TEST RUNNER CONFIGURATION
TEST_RUNNER = 'django.test.runner.DiscoverRunner'
########## END TEST RUNNER CONFIGURATION
########## APP CONFIGURATION
DJANGO_APPS = (
# Default Django apps:
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
# Useful template tags:
# 'django.contrib.humanize',
# Admin panel and documentation:
'django.contrib.admin',
# 'django.contrib.admindocs',
)
# Apps specific for this project go here.
LOCAL_APPS = (
'ldapentities',
'ldaptasks',
)
# See: https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
INSTALLED_APPS = DJANGO_APPS + LOCAL_APPS
########## END APP CONFIGURATION
########## LOGGING CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#logging
# A sample logging configuration. The only tangible logging
# performed by this configuration is to send an email to
# the site admins on every HTTP 500 error when DEBUG=False.
# See http://docs.djangoproject.com/en/dev/topics/logging for
# more details on how to customize your logging configuration.
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse'
}
},
'handlers': {
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'django.utils.log.AdminEmailHandler'
}
},
'loggers': {
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': True,
},
}
}
########## END LOGGING CONFIGURATION
########## WSGI CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#wsgi-application
WSGI_APPLICATION = '%s.wsgi.application' % SITE_NAME
########## END WSGI CONFIGURATION
########## LDAP SETTINGS
GROUP_BASE_DN = get_env_setting('GVALDAP_BASEDN_GROUP')
USER_BASE_DN = get_env_setting('GVALDAP_BASEDN_USER')
########## END LDAP SETTINGS
########## CELERY CONFIGURATION
BROKER_URL = get_env_setting('GVALDAP_BROKER_URL')
CELERY_RESULT_BACKEND = 'amqp'
CELERY_RESULT_PERSISTENT = True
CELERY_TASK_RESULT_EXPIRES = None
CELERY_ROUTES = (
'gvacommon.celeryrouters.GvaRouter',
)
CELERY_TIMEZONE = 'Europe/Berlin'
CELERY_ENABLE_UTC = True
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERAILIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
########## END CELERY CONFIGURATION

View file

@ -1,51 +0,0 @@
# pymode:lint_ignore=W0401,E501
"""
Development settings and globals based on :py:mod:`gvaldap.settings.base`.
"""
from __future__ import absolute_import
from .base import *
########## DEBUG CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#debug
DEBUG = True
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-debug
TEMPLATE_DEBUG = DEBUG
########## END DEBUG CONFIGURATION
########## EMAIL CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
########## END EMAIL CONFIGURATION
########## CACHE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#caches
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
}
}
########## END CACHE CONFIGURATION
########## TOOLBAR CONFIGURATION
# See: http://django-debug-toolbar.readthedocs.org/en/latest/installation.html#explicit-setup
INSTALLED_APPS += (
'debug_toolbar',
)
MIDDLEWARE_CLASSES += (
'debug_toolbar.middleware.DebugToolbarMiddleware',
)
DEBUG_TOOLBAR_PATCH_SETTINGS = False
# http://django-debug-toolbar.readthedocs.org/en/latest/installation.html
INTERNAL_IPS = ('127.0.0.1',)
########## END TOOLBAR CONFIGURATION

View file

@ -1,50 +0,0 @@
# pymode:lint_ignore=W0401,E501
"""
Production settings and globals based on :py:mod:`gvaldap.settings.base`.
"""
from __future__ import absolute_import
from .base import *
########## HOST CONFIGURATION
# See: https://docs.djangoproject.com/en/1.5/releases/1.5/#allowed-hosts-required-in-production
ALLOWED_HOSTS = get_env_setting('GVALDAP_ALLOWED_HOSTS').split(',')
########## END HOST CONFIGURATION
########## EMAIL CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-host
#EMAIL_HOST = environ.get('EMAIL_HOST', 'smtp.gmail.com')
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-host-password
#EMAIL_HOST_PASSWORD = environ.get('EMAIL_HOST_PASSWORD', '')
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-host-user
#EMAIL_HOST_USER = environ.get('EMAIL_HOST_USER', 'your_email@example.com')
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-port
#EMAIL_PORT = environ.get('EMAIL_PORT', 587)
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-subject-prefix
EMAIL_SUBJECT_PREFIX = '[%s] ' % SITE_NAME
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-use-tls
#EMAIL_USE_TLS = True
# See: https://docs.djangoproject.com/en/dev/ref/settings/#server-email
SERVER_EMAIL = get_env_setting('GVALDAP_SERVER_EMAIL')
########## END EMAIL CONFIGURATION
########## DATABASE CONFIGURATION
#DATABASES = {}
########## END DATABASE CONFIGURATION
########## CACHE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#caches
#CACHES = {}
########## END CACHE CONFIGURATION

View file

@ -1,20 +0,0 @@
# pymode:lint_ignore=W0401
"""
Test settings based on :py:mod:`gvaldap.settings.base`.
"""
from __future__ import absolute_import
from .base import *
########## IN-MEMORY TEST DATABASE
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": ":memory:",
"USER": "",
"PASSWORD": "",
"HOST": "",
"PORT": "",
},
}

View file

@ -0,0 +1,12 @@
"""
This module provides tests for :py:mod:`gvaldap.wsgi`.
"""
from unittest import TestCase
class WSGITest(TestCase):
def test_wsgi_application(self):
from gvaldap import wsgi
self.assertIsNotNone(wsgi.application)

View file

@ -3,23 +3,27 @@ This module defines the main URLConf for gvaldap.
"""
from django.conf.urls import patterns, include, url
from django.conf import settings
# Uncomment the next two lines to enable the admin:
from django.conf.urls import include, url
from django.contrib import admin
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
admin.autodiscover()
urlpatterns = patterns(
'',
url(r'^admin/', include(admin.site.urls)),
)
urlpatterns = [
url(r'^admin/', admin.site.urls),
]
# Uncomment the next line to serve media files in dev.
# urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
if settings.DEBUG:
if settings.DEBUG: # pragma: no cover
import debug_toolbar
urlpatterns += patterns('',
url(r'^__debug__/', include(debug_toolbar.urls)),
)
urlpatterns = (
[
url(r'^__debug__/', include(debug_toolbar.urls)),
]
+ staticfiles_urlpatterns()
+ urlpatterns
)

View file

@ -17,6 +17,11 @@ import os
from os.path import abspath, dirname
from sys import path
# This application object is used by any WSGI server configured to use this
# file. This includes Django's development server, if the WSGI_APPLICATION
# setting points here.
from django.core.wsgi import get_wsgi_application # noqa
SITE_ROOT = dirname(dirname(abspath(__file__)))
path.append(SITE_ROOT)
@ -24,12 +29,8 @@ path.append(SITE_ROOT)
# if running multiple sites in the same mod_wsgi process. To fix this, use
# mod_wsgi daemon mode with each site in its own daemon process, or use
# os.environ["DJANGO_SETTINGS_MODULE"] = "jajaja.settings"
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gvaldap.settings.production")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gvaldap.settings")
from django.core.wsgi import get_wsgi_application
# This application object is used by any WSGI server configured to use this
# file. This includes Django's development server, if the WSGI_APPLICATION
# setting points here.
application = get_wsgi_application()
# Apply WSGI middleware here.

View file

@ -7,7 +7,7 @@ Admin classes for easy `django admin`_ based administration of LDAP entities.
from django.contrib import admin
from .models import (
from ldapentities.models import (
LdapGroup,
LdapUser,
)

View file

@ -6,9 +6,9 @@ The models are based on :py:class:`ldapmodels.Model` from `django-ldapdb`_.
.. _django-ldapdb: https://github.com/jlaine/django-ldapdb#readme
"""
from __future__ import unicode_literals
from django.conf import settings
from django.utils.encoding import python_2_unicode_compatible
from ldapdb.models.fields import (
CharField,
IntegerField,
@ -19,7 +19,6 @@ import ldapdb.models as ldapmodels
from passlib.hash import ldap_salted_sha1
@python_2_unicode_compatible
class LdapGroup(ldapmodels.Model):
"""
Class for representing an LDAP group entity with objectClass `posixGroup`.
@ -56,7 +55,6 @@ class LdapGroup(ldapmodels.Model):
return self.name
@python_2_unicode_compatible
class LdapUser(ldapmodels.Model):
"""
Class for representing an LDAP user entity with objectClasses `account` and

View file

View file

@ -0,0 +1,69 @@
"""
This module provides tests for :py:mod:`ldapentities.admin`.
"""
from __future__ import absolute_import
import volatildap
from django.conf import settings
from django.test import TestCase
from django.contrib.auth import get_user_model
from django.urls import reverse
User = get_user_model()
TEST_USER = 'admin'
TEST_EMAIL = 'admin@example.org'
TEST_PASSWORD = 'secret'
admin = (settings.DATABASES['ldap']['USER'], {
'objectClass': ['person'],
'userPassword': [
settings.DATABASES['ldap'][
'PASSWORD']],
'sn': 'Admin',
})
groups = (settings.GROUP_BASE_DN, {
'objectClass': ['top', 'organizationalUnit'], 'ou': ['groups']})
users = (
settings.USER_BASE_DN, {
'objectClass': ['top', 'organizationalUnit'], 'ou': ['users']})
class LdapUserAdminTest(TestCase):
databases = ["default", "ldap"]
directory = dict([admin, groups, users])
@classmethod
def setUpClass(cls):
super(LdapUserAdminTest, cls).setUpClass()
cls.ldap_server = volatildap.LdapServer(
initial_data=cls.directory,
schemas=['core.schema', 'cosine.schema', 'inetorgperson.schema',
'nis.schema'],
)
settings.DATABASES['ldap']['USER'] = cls.ldap_server.rootdn
settings.DATABASES['ldap']['PASSWORD'] = cls.ldap_server.rootpw
settings.DATABASES['ldap']['NAME'] = cls.ldap_server.uri
@classmethod
def tearDownClass(cls):
cls.ldap_server.stop()
super(LdapUserAdminTest, cls).tearDownClass()
def setUp(self):
User.objects.create_superuser(
TEST_USER, email=TEST_EMAIL, password=TEST_PASSWORD)
self.client.login(username=TEST_USER, password=TEST_PASSWORD)
self.ldap_server.start()
def test_can_administer_groups(self):
response = self.client.get(reverse(
'admin:ldapentities_ldapgroup_changelist'))
self.assertEqual(response.status_code, 200)
def test_can_administer_users(self):
response = self.client.get(reverse(
'admin:ldapentities_ldapuser_changelist'))
self.assertEqual(response.status_code, 200)

View file

@ -0,0 +1,37 @@
"""
This model provides tests for :py:mod:`ldapentities.models`.
"""
from __future__ import absolute_import, unicode_literals
from django.test import TestCase
from passlib.hash import ldap_salted_sha1
from ldapentities.models import LdapGroup, LdapUser
class LdapGroupTest(TestCase):
def test___str__(self):
ldapgroup = LdapGroup(
gid=5000, name='test', description='test group')
self.assertEqual(str(ldapgroup), 'test')
class LdapUserTest(TestCase):
def test___str__(self):
ldapuser = LdapUser(
uid=5000, group=5000, gecos="a test user",
home_directory='/home/test', login_shell='/bin/bash',
username='test', password='test', common_name='Test')
self.assertEqual(str(ldapuser), 'test')
def test_set_password(self):
ldapuser = LdapUser(
uid=5000, group=5000, gecos="a test user",
home_directory='/home/test', login_shell='/bin/bash',
username='test', password='test', common_name='Test')
self.assertEqual(ldapuser.password, 'test')
ldapuser.set_password('test2')
self.assertTrue(ldap_salted_sha1.verify('test2', ldapuser.password))

View file

@ -0,0 +1,17 @@
"""
This module defines the Celery_ app for gvaldap.
.. _Celery: http://www.celeryproject.org/
"""
import os
from celery import Celery
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gvaldap.settings")
app = Celery("ldaptasks")
app.config_from_object("django.conf:settings", namespace="CELERY")
app.autodiscover_tasks()

View file

@ -7,22 +7,22 @@ This module defines `Celery`_ tasks to manage LDAP entities.
from __future__ import absolute_import
from django.core.exceptions import ObjectDoesNotExist
from copy import deepcopy
from celery import shared_task
from celery.utils.log import get_task_logger
from celery.exceptions import Reject
from celery.utils.log import get_task_logger
from ldapentities.models import (
LdapGroup,
LdapUser,
)
from django.core.exceptions import ObjectDoesNotExist
from django.db.utils import Error as DjangoDBUtilsError
from ldapentities.models import LdapGroup, LdapUser
_LOGGER = get_task_logger(__name__)
_logger = get_task_logger(__name__)
@shared_task
def create_ldap_group(groupname, gid, descr):
@shared_task(autoretry_for=(DjangoDBUtilsError,), default_retry_delay=10)
def create_ldap_group(groupname, gid, description):
"""
This task creates an :py:class:`LDAP group <ldapentities.models.LdapGroup>`
if it does not exist yet.
@ -32,24 +32,34 @@ def create_ldap_group(groupname, gid, descr):
: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
:param str description: description text for the group
:return: dictionary containing groupname, gid, description and
:py:const:`group_dn` set to the distinguished name of the newly created
or existing LDAP group
:rtype: dict
"""
try:
ldapgroup = LdapGroup.objects.get(name=groupname)
_logger.info(
'ldap group with dn {0} already exists'.format(ldapgroup.dn)
_LOGGER.info(
"LDAP group %s with groupname %s already exists", ldapgroup.dn, groupname
)
ldapgroup.gid = gid
except LdapGroup.DoesNotExist:
ldapgroup = LdapGroup(gid=gid, name=groupname)
ldapgroup.description = descr
_LOGGER.info("created LDAP group %s", ldapgroup.dn)
ldapgroup.description = description
ldapgroup.save()
return ldapgroup.dn
_LOGGER.info("set description of LDAP group %s", ldapgroup.dn)
return {
"groupname": groupname,
"gid": gid,
"description": description,
"group_dn": ldapgroup.dn,
}
@shared_task
@shared_task(autoretry_for=(DjangoDBUtilsError,), default_retry_delay=10)
def create_ldap_user(username, uid, gid, gecos, homedir, shell, password):
"""
This task creates an :py:class:`LDAP user <ldapentities.models.LdapUser>`
@ -69,21 +79,23 @@ def create_ldap_user(username, uid, gid, gecos, homedir, shell, password):
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
:return: dictionary containing username, uid, gid, gecos, homedir, shell,
password and :py:const:`user_dn` set to the distinguished name of the
newly created or existing LDAP user
:rtype: dict
"""
try:
ldapuser = LdapUser.objects.get(username=username)
_logger.info(
'ldap user with dn {0} already exists'.format(ldapuser.dn)
_LOGGER.info(
"LDAP user %s with username %s already exists", ldapuser.dn, username
)
except LdapUser.DoesNotExist:
ldapuser = LdapUser(username=username)
try:
ldapgroup = LdapGroup.objects.get(gid=gid)
except ObjectDoesNotExist as exc:
_logger.info('ldap group with gid {0} does not exist')
_LOGGER.error("LDAP group with gid %d does not exist", gid)
raise Reject(exc, requeue=False)
ldapuser.uid = uid
ldapuser.group = gid
@ -94,39 +106,58 @@ def create_ldap_user(username, uid, gid, gecos, homedir, shell, password):
ldapuser.common_name = username
if password is not None:
ldapuser.set_password(password)
_LOGGER.info("set password for LDAP user %s", ldapuser.dn)
ldapuser.save()
_LOGGER.info("LDAP user %s created", ldapuser.dn)
if ldapuser.username in ldapgroup.members:
_logger.info('user {0} is already member of {1}'.format(
ldapuser.username, ldapgroup.dn)
_LOGGER.info(
"LDAP user %s is already member of LDAP group %s", ldapuser.dn, ldapgroup.dn
)
else:
ldapgroup.members.append(ldapuser.username)
ldapgroup.save()
ldapuser.save()
return ldapuser.dn
_LOGGER.info(
"LDAP user %s has been added to LDAP group %s", ldapuser.dn, ldapgroup.dn
)
return {
"username": username,
"uid": uid,
"gid": gid,
"gecos": gecos,
"homedir": homedir,
"shell": shell,
"user_dn": ldapuser.dn,
}
@shared_task(bind=True)
def set_ldap_user_password(self, username, password):
@shared_task(autoretry_for=(DjangoDBUtilsError,), default_retry_delay=10)
def set_ldap_user_password(username, password):
"""
This task sets the password of an existing :py:class:`LDAP user
<ldapentities.models.LdapUser>`.
:param str username: the user name
:param str password: teh clear text password
:return: :py:const:`True` if the password has been set, :py:const:`False`
if the user does not exist.
:return: dictionary containing the username and a flag
:py:const:`password_set` that is set to :py:const:`True` if the
password has been set, :py:const:`False` if the user does not exist.
:rtype: dict
"""
retval = {"username": username, "password_set": False}
try:
ldapuser = LdapUser.objects.get(username=username)
except LdapUser.DoesNotExist:
return False
_LOGGER.info("there is no LDAP user with username %s", username)
return retval
ldapuser.set_password(password)
ldapuser.save()
return True
_LOGGER.info("set new password for LDAP user %s", ldapuser.dn)
retval["password_set"] = True
return retval
@shared_task(bind=True)
@shared_task(bind=True, autoretry_for=(DjangoDBUtilsError,), default_retry_delay=10)
def add_ldap_user_to_group(self, username, groupname):
"""
This task adds the specified user to the given group.
@ -137,126 +168,198 @@ def add_ldap_user_to_group(self, username, groupname):
: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
:return: dictionary containing the username, groupname and a flag
:py:const`added` that is as a :py:const:`True` if the user has been
added to the group otherwise to :py:const:`False`
:rtype: dict
"""
retval = {"username": username, "groupname": groupname, "added": False}
try:
ldapgroup = LdapGroup.objects.get(name=groupname)
ldapuser = LdapUser.objects.get(username=username)
except LdapGroup.DoesNotExist:
_logger.error('ldap group {0} does not exist'.format(groupname))
_LOGGER.error("LDAP group with groupname %s does not exist", groupname)
except LdapUser.DoesNotExist as exc:
_logger.error('ldap user {0} does not exist'.format(username))
_LOGGER.error("LDAP user with username %s does not exist", username)
self.retry(exc=exc, time_limit=5)
else:
if not ldapuser.username in ldapgroup.members:
if ldapuser.username not in ldapgroup.members:
ldapgroup.members.append(ldapuser.username)
ldapgroup.save()
else:
_logger.info('ldap user {0} is already in group {1}'.format(
ldapuser.username, ldapgroup.dn)
_LOGGER.info(
"LDAP user %s has been added to LDAP group %s",
ldapuser.username,
ldapgroup.dn,
)
return True
return False
else:
_LOGGER.info(
"LDAP user %s is already in LDAP group %s",
ldapuser.username,
ldapgroup.dn,
)
retval["added"] = True
return retval
@shared_task
@shared_task(autoretry_for=(DjangoDBUtilsError,), default_retry_delay=10)
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
:return: dictionary containing the input parameters and a flag
:py:const:`removed` that is set to :py:const:`True` if the user has
been removed, False otherwise
:rtype: dict
"""
ldapgroup = LdapGroup.objects.get(name=groupname)
ldapuser = LdapUser.objects.get(username=username)
performdelete = ldapuser.username in ldapgroup.members
if performdelete:
ldapgroup.members.remove(ldapuser.username)
ldapgroup.save()
return performdelete
retval = {"username": username, "groupname": groupname, "removed": False}
try:
ldapgroup = LdapGroup.objects.get(name=groupname)
ldapuser = LdapUser.objects.get(username=username)
except LdapGroup.DoesNotExist:
_LOGGER.error("LDAP group with groupname %s does not exist", groupname)
except LdapUser.DoesNotExist:
_LOGGER.error("LDAP user with username %s does not exist", username)
else:
if ldapuser.username in ldapgroup.members:
ldapgroup.members.remove(ldapuser.username)
_LOGGER.info(
"removed LDAP user %s from LDAP group %s", ldapuser.dn, ldapgroup.dn
)
ldapgroup.save()
retval["removed"] = True
else:
_LOGGER.info(
"LDAP user %s is not a member of LDAP group %s",
ldapuser.dn,
ldapgroup.dn,
)
return retval
@shared_task
def delete_ldap_user(username):
@shared_task(autoretry_for=(DjangoDBUtilsError,), default_retry_delay=10)
def delete_ldap_user(username, *args, **kwargs):
"""
This task deletes the given user.
:param str username: the user name
:return: True if the user has been deleted, False otherwise
:rtype: boolean
:return: dictionary containing the username and a flag :py:const:`deleted`
that is set to :py:const:`True` if the user has been deleted and is set
to :py:const:`False` otherwise
:rtype: dict
.. note::
This variant can only be used at the beginning of a Celery task chain
or as a standalone task.
Use :py:func:`ldaptasks.tasks.delete_ldap_user_chained` at other
positions in the task chain
"""
retval = {"username": username, "deleted": False}
try:
ldapuser = LdapUser.objects.get(username=username)
except LdapUser.DoesNotExist:
_logger.info('there is no ldap user with uid {0}'.format(
username)
)
_LOGGER.info("there is no LDAP user with username %s", username)
else:
try:
ldapgroup = LdapGroup.objects.get(gid=ldapuser.group)
except LdapGroup.DoesNotExist:
_logger.info('group {0} for user {1} does not exist'.format(
ldapuser.group, ldapuser.username)
_LOGGER.info(
"LDAP group %s of LDAP user %s does not exist",
ldapuser.group,
ldapuser.dn,
)
else:
if ldapuser.username in ldapgroup.members:
ldapgroup.members.remove(ldapuser.username)
ldapgroup.save()
_LOGGER.info(
"removed LDAP user %s from LDAP group %s", ldapuser.dn, ldapgroup.dn
)
userdn = ldapuser.dn
ldapuser.delete()
return True
return False
_LOGGER.info("deleted LDAP user %s", userdn)
retval["deleted"] = True
return retval
@shared_task
@shared_task(autoretry_for=(DjangoDBUtilsError,), default_retry_delay=10)
def delete_ldap_user_chained(previous_result, *args, **kwargs):
"""
This task deletes the given user.
:param dict previous_result: a dictionary describing the result of the
previous step in the Celery task chain. This dictionary must contain a
:py:const:`username` key
:return: a copy of the :py:obj:`previous_result` dictionary with a new
:py:const:`deleted` key set to :py:const:`True` if the user has been
deleted and set to :py:const:`False` otherwise
:rtype: dict
"""
username = previous_result["username"]
retval = deepcopy(previous_result)
retval.update(delete_ldap_user(username))
return retval
@shared_task(autoretry_for=(DjangoDBUtilsError,), default_retry_delay=10)
def delete_ldap_group_if_empty(groupname):
"""
This task deletes the given group if it is empty.
:param str groupname: the group name
:return: dictionary that contains the groupname and a flag
:py:const:`deleted` that is set to :py:const:`True` if the group has
been deleted and is set to :py:const:`False` otherwise
:rtype: dict
"""
retval = {"groupname": groupname, "deleted": False}
try:
ldapgroup = LdapGroup.objects.get(name=groupname)
except LdapGroup.DoesNotExist:
_LOGGER.info("LDAP group with groupname %s does not exist", groupname)
else:
if len(ldapgroup.members) == 0:
groupdn = ldapgroup.dn
ldapgroup.delete()
_LOGGER.info("deleted LDAP group %s", groupdn)
retval["deleted"] = True
else:
_LOGGER.info(
"LDAP group %s has not been deleted. It still has %d members",
ldapgroup.dn,
len(ldapgroup.members),
)
return retval
@shared_task(autoretry_for=(DjangoDBUtilsError,), default_retry_delay=10)
def delete_ldap_group(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
:return: dictionary that contains the groupname and a flag
:py:const:`deleted` that is set to :py:const:`True` if the group has
been deleted and is set to :py:const:`False` otherwise
:rtype: dict
"""
retval = {"groupname": groupname, "deleted": False}
try:
ldapgroup = LdapGroup.objects.get(name=groupname)
except LdapGroup.DoesNotExist:
_logger.info('ldap group with name {0} does not exist'.format(
groupname)
)
else:
if len(ldapgroup.members) == 0:
ldapgroup.delete()
return True
else:
_logger.info('ldap group {0} still has {1} members'.format(
ldapgroup.dn, len(ldapgroup.members))
)
return False
@shared_task
def delete_ldap_group(groupname):
"""
This taks deletes the given group.
:param str groupname: the group name
:return: True if the user has been deleted, False otherwise
:rtype: boolean
"""
try:
ldapgroup = LdapGroup.objects.get(name=groupname)
except LdapGroup.DoesNotExist:
_logger.info('ldap group with name {0} does not exist'.format(
groupname)
)
_LOGGER.info("LDAP group with name %s does not exist", groupname)
else:
groupdn = ldapgroup.dn
ldapgroup.delete()
return True
return False
_LOGGER.info("deleted LDAP group %s", groupdn)
retval["deleted"] = True
return retval

View file

View file

@ -0,0 +1,264 @@
"""
This module provides tests for :py:mod:`ldaptasks.tasks`.
"""
import volatildap
from celery.exceptions import Reject
from django.conf import settings
from django.test import TestCase
from ldapentities.models import LdapUser
from ldaptasks.tasks import (
add_ldap_user_to_group,
create_ldap_group,
create_ldap_user,
delete_ldap_group,
delete_ldap_group_if_empty,
delete_ldap_user,
delete_ldap_user_chained,
remove_ldap_user_from_group,
set_ldap_user_password,
)
class LdapTaskTestCase(TestCase):
databases = ["default", "ldap"]
directory = {
settings.DATABASES['ldap']['USER']: {
'objectClass': ['person'],
'userPassword': [settings.DATABASES['ldap']['PASSWORD']],
'sn': 'Admin',
},
settings.GROUP_BASE_DN: {
'objectClass': ['top', 'organizationalUnit'],
'ou': ['groups']
},
settings.USER_BASE_DN: {
'objectClass': ['top', 'organizationalUnit'],
'ou': ['users']
},
'cn=existing,' + settings.GROUP_BASE_DN: {
'objectClass': ['posixGroup'],
'gidNumber': ['4711'],
'cn': ['existing'],
'description': ['existing test group'],
'memberUid': ['existing'],
},
'uid=existing,' + settings.USER_BASE_DN: {
'objectClass': ['account', 'posixAccount'],
'uidNumber': ['815'],
'gidNumber': ['4711'],
'gecos': ['existing test user'],
'homeDirectory': ['/home/existing'],
'loginShell': ['/bin/bash'],
'uid': ['existing'],
'userPassword': ['secret'],
'cn': ['existing']
}
}
@classmethod
def setUpClass(cls):
super(LdapTaskTestCase, cls).setUpClass()
cls.ldap_server = volatildap.LdapServer(
initial_data=cls.directory,
schemas=['core.schema', 'cosine.schema', 'inetorgperson.schema',
'nis.schema'],
)
settings.DATABASES['ldap']['USER'] = cls.ldap_server.rootdn
settings.DATABASES['ldap']['PASSWORD'] = cls.ldap_server.rootpw
settings.DATABASES['ldap']['NAME'] = cls.ldap_server.uri
@classmethod
def tearDownClass(cls):
cls.ldap_server.stop()
super(LdapTaskTestCase, cls).tearDownClass()
def setUp(self):
self.ldap_server.start()
def test_create_ldap_group(self):
result = create_ldap_group('test', 5000, 'test group')
self.assertEqual({
'groupname': 'test', 'gid': 5000, 'description': 'test group',
'group_dn': 'cn=test,%s' % settings.GROUP_BASE_DN
}, result)
def test_create_ldap_group_existing(self):
result = create_ldap_group('existing', 4711, 'existing test group')
self.assertEqual({
'groupname': 'existing', 'gid': 4711,
'description': 'existing test group',
'group_dn': 'cn=existing,%s' % settings.GROUP_BASE_DN
}, result)
def test_create_ldap_group_existing_modify(self):
result = create_ldap_group(
'existing', 4711, 'change existing test group')
self.assertEqual({
'groupname': 'existing', 'gid': 4711,
'description': 'change existing test group',
'group_dn': 'cn=existing,%s' % settings.GROUP_BASE_DN
}, result)
def test_create_ldap_user(self):
result = create_ldap_user(
'test', 5000, 4711, 'Test User', '/home/test', '/bin/bash',
'secret')
self.assertEqual({
'username': 'test', 'uid': 5000, 'gid': 4711, 'gecos': 'Test User',
'homedir': '/home/test', 'shell': '/bin/bash',
'user_dn': 'uid=test,%s' % settings.USER_BASE_DN
}, result)
def test_create_ldap_user_invalid_group(self):
with self.assertRaises(Reject):
create_ldap_user(
'test', 5000, 5000, 'Test User', '/home/test', '/bin/bash',
'secret')
def test_create_ldap_user_no_password(self):
result = create_ldap_user(
'test', 5000, 4711, 'Test User', '/home/test', '/bin/bash',
None)
self.assertEqual({
'username': 'test', 'uid': 5000, 'gid': 4711, 'gecos': 'Test User',
'homedir': '/home/test', 'shell': '/bin/bash',
'user_dn': 'uid=test,%s' % settings.USER_BASE_DN
}, result)
def test_create_ldap_user_existing(self):
result = create_ldap_user(
'existing', 815, 4711, 'existing test user', '/home/existing',
'/bin/bash', 'secret'
)
self.assertEqual({
'username': 'existing', 'uid': 815, 'gid': 4711,
'gecos': 'existing test user', 'homedir': '/home/existing',
'shell': '/bin/bash',
'user_dn': u'uid=existing,%s' % settings.USER_BASE_DN
}, result)
def test_set_ldap_user_password_existing(self):
result = set_ldap_user_password('existing', 'newpassword')
self.assertEqual({
'username': 'existing', 'password_set': True
}, result)
def test_set_ldap_user_password_missing(self):
result = set_ldap_user_password('missing', 'newpassword')
self.assertEqual({
'username': 'missing', 'password_set': False
}, result)
def test_add_ldap_user_to_group_existing(self):
result = add_ldap_user_to_group('existing', 'existing')
self.assertEqual({
'username': 'existing', 'groupname': 'existing', 'added': True
}, result)
def test_add_ldap_user_to_group_new_user(self):
create_ldap_group('test', 5000, 'test group')
result = add_ldap_user_to_group('existing', 'test')
self.assertEqual({
'username': 'existing', 'groupname': 'test', 'added': True
}, result)
def test_add_ldap_user_to_group_no_group(self):
result = add_ldap_user_to_group('existing', 'test')
self.assertEqual({
'username': 'existing', 'groupname': 'test', 'added': False
}, result)
def test_add_ldap_user_to_group_no_user(self):
with self.assertRaises(LdapUser.DoesNotExist):
add_ldap_user_to_group('test', 'existing')
def test_remove_ldap_user_from_group_existing(self):
result = remove_ldap_user_from_group('existing', 'existing')
self.assertEqual({
'username': 'existing', 'groupname': 'existing', 'removed': True
}, result)
self.assertNotIn('memberUid', self.ldap_server.get(
'cn=existing,' + settings.GROUP_BASE_DN))
def test_remove_ldap_user_from_group_not_in_group(self):
create_ldap_group('test', 5000, 'test group')
result = remove_ldap_user_from_group('existing', 'test')
self.assertEqual({
'username': 'existing', 'groupname': 'test', 'removed': False
}, result)
def test_remove_ldap_user_from_group_no_group(self):
result = remove_ldap_user_from_group('existing', 'test')
self.assertEqual({
'username': 'existing', 'groupname': 'test', 'removed': False
}, result)
def test_remove_ldap_user_from_group_no_user(self):
result = remove_ldap_user_from_group('test', 'existing')
self.assertEqual({
'username': 'test', 'groupname': 'existing', 'removed': False
}, result)
def test_delete_ldap_user_existing(self):
result = delete_ldap_user('existing')
self.assertEqual({'username': 'existing', 'deleted': True}, result)
with self.assertRaises(KeyError):
self.ldap_server.get('uid=existing,' + settings.USER_BASE_DN)
self.assertNotIn('memberUid', self.ldap_server.get(
'cn=existing,' + settings.GROUP_BASE_DN))
def test_delete_ldap_user_missing(self):
result = delete_ldap_user('missing')
self.assertEqual({'username': 'missing', 'deleted': False}, result)
def test_delete_ldap_user_no_group(self):
self.ldap_server.get('uid=existing,' + settings.USER_BASE_DN)[
'gidNumber'] = '5000'
result = delete_ldap_user('existing')
self.assertEqual({'username': 'existing', 'deleted': True}, result)
with self.assertRaises(KeyError):
self.ldap_server.get('uid=existing,' + settings.USER_BASE_DN)
def test_delete_ldap_user_chained_exsting(self):
result = delete_ldap_user_chained({'username': 'existing'})
self.assertEqual({'username': 'existing', 'deleted': True}, result)
with self.assertRaises(KeyError):
self.ldap_server.get('uid=existing,' + settings.USER_BASE_DN)
group_object = self.ldap_server.get('cn=existing,' + settings.GROUP_BASE_DN)
self.assertNotIn('memberUid', group_object)
def test_delete_ldap_group_if_empty_nonempty(self):
result = delete_ldap_group_if_empty('existing')
self.assertEqual({'groupname': 'existing', 'deleted': False}, result)
ldap_object = self.ldap_server.get('cn=existing,' + settings.GROUP_BASE_DN)
self.assertIsNotNone(ldap_object)
def test_delete_ldap_group_if_empty_missing(self):
result = delete_ldap_group_if_empty('missing')
self.assertEqual({'groupname': 'missing', 'deleted': False}, result)
def test_delete_ldap_group_if_empty_empty(self):
self.ldap_server.add({'cn=emptygroup,' + settings.GROUP_BASE_DN: {
'objectClass': ['posixGroup'],
'gidNumber': ['4712'],
'cn': ['existing'],
'description': ['existing test group'],
}})
result = delete_ldap_group_if_empty('emptygroup')
self.assertEqual({'groupname': 'emptygroup', 'deleted': True}, result)
with self.assertRaises(KeyError):
self.ldap_server.get('cn=emptygroup,' + settings.GROUP_BASE_DN)
def test_delete_ldap_group_existing(self):
result = delete_ldap_group('existing')
self.assertEqual({'groupname': 'existing', 'deleted': True}, result)
with self.assertRaises(KeyError):
self.ldap_server.get('cn=existing,' + settings.GROUP_BASE_DN)
def test_delete_ldap_group_missing(self):
result = delete_ldap_group('missing')
self.assertEqual({'groupname': 'missing', 'deleted': False}, result)

View file

@ -3,7 +3,7 @@ import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gvaldap.settings.local")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gvaldap.settings")
from django.core.management import execute_from_command_line

25
gvaldap/setup.cfg Normal file
View file

@ -0,0 +1,25 @@
[pep8]
exclude = migrations
[flake8]
exclude = migrations
[coverage:run]
source = gvaldap,ldapentities,ldaptasks
branch = True
omit = */migrations/*,*/tests/*.py,*/tests.py,gvaldap.py
relative_files = True
[coverage:report]
show_missing = True
[coverage:html]
directory = ../coverage-report
[isort]
multi_line_output = 3
line_length = 88
known_django = django
known_third_party = celery,volatildap
include_trailing_comma = True
sections = FUTURE,STDLIB,THIRDPARTY,DJANGO,FIRSTPARTY,LOCALFOLDER

View file

@ -1,3 +0,0 @@
# This file is here because many Platforms as a Service look for
# requirements.txt in the root directory of a project.
-r requirements/production.txt

View file

@ -1,9 +0,0 @@
Django==1.7.1
django-ldapdb==0.3.2
bpython==0.13.1
django-braces==1.4.0
django-model-utils==2.2
logutils==0.3.3
celery==3.1.17
passlib==1.6.2
pyaml==14.12.10

View file

@ -1,7 +0,0 @@
# Local development dependencies go here
-r base.txt
coverage==3.7.1
django-debug-toolbar==1.2.2
Sphinx==1.2.3
sqlparse==0.1.14
releases==0.7.0

View file

@ -1,3 +0,0 @@
# Pro-tip: Try not to put anything here. There should be no dependency in
# production that isn't in development.
-r base.txt

View file

@ -1,3 +0,0 @@
# Test dependencies go here.
-r base.txt
coverage==3.7.1

10
salt/grains Normal file
View file

@ -0,0 +1,10 @@
gnuviechadmin:
user: vagrant
group: vagrant
checkout: /vagrant
home: /home/vagrant
update_git: False
roles:
- vagrant
- ldapserver
- gnuviechadmin.gvaldap

11
salt/minion Normal file
View file

@ -0,0 +1,11 @@
file_client: local
file_roots:
base:
- /srv/salt/
pillar_roots:
base:
- /srv/pillar
log_file: file:///dev/log