mirror of
https://github.com/kakwa/ldapcherry
synced 2025-03-29 11:01:13 +01:00
Compare commits
185 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
662e09eccf | ||
![]() |
dd331f948c | ||
![]() |
a2e985576b | ||
![]() |
6a2e6e56d0 | ||
![]() |
5d8fc08f5b | ||
![]() |
59855f0090 | ||
![]() |
d1215bc56f | ||
![]() |
ef11e6a7e3 | ||
![]() |
3596e14249 | ||
![]() |
b94713acb6 | ||
![]() |
de16df475f | ||
![]() |
242c9ab96e | ||
![]() |
48dbff983d | ||
![]() |
6413b782dd | ||
![]() |
55c49b4eff | ||
![]() |
53a4b6fd5e | ||
![]() |
0ccbe95d9a | ||
![]() |
4da050236d | ||
![]() |
135699bd48 | ||
![]() |
1549a172f7 | ||
![]() |
e2ab3e85d8 | ||
![]() |
b5907ce340 | ||
![]() |
791895d4c3 | ||
![]() |
b12add4a0f | ||
![]() |
856157af79 | ||
![]() |
b5e7cb6a44 | ||
![]() |
d61c89460e | ||
![]() |
0a96ca61d5 | ||
![]() |
3b58f1464e | ||
![]() |
50c6259035 | ||
![]() |
245bafb01c | ||
![]() |
5ee8a74040 | ||
![]() |
96acda7aa6 | ||
![]() |
30c28c5feb | ||
![]() |
dc60300a29 | ||
![]() |
882a303474 | ||
![]() |
d831b09293 | ||
![]() |
7ac7118c9a | ||
![]() |
d690bbdc41 | ||
![]() |
73c02ccff4 | ||
![]() |
799ca2403f | ||
![]() |
bbafafae60 | ||
![]() |
0cf5483785 | ||
![]() |
df2746b996 | ||
![]() |
e6bcf9d97d | ||
![]() |
57bcaaed66 | ||
![]() |
b68214022c | ||
![]() |
932e7a8b40 | ||
![]() |
abf1454278 | ||
![]() |
0793361d90 | ||
![]() |
f824790849 | ||
![]() |
7390c931b9 | ||
![]() |
4a8aa1c655 | ||
![]() |
e50df5dde3 | ||
![]() |
fba2d32b44 | ||
![]() |
7a8468f8b1 | ||
![]() |
9d0d321e9b | ||
![]() |
c5536bdc56 | ||
![]() |
abfce4803a | ||
![]() |
046afbbe29 | ||
![]() |
98fca30fba | ||
![]() |
f13961790f | ||
![]() |
a56c491ee1 | ||
![]() |
02357d886a | ||
![]() |
263e6be547 | ||
![]() |
05aace0e9d | ||
![]() |
baa3430e63 | ||
![]() |
90ff69586b | ||
![]() |
79983c078f | ||
![]() |
10747cff93 | ||
![]() |
979d4eeda8 | ||
![]() |
fb6b0a5d31 | ||
![]() |
bbfe96d4f7 | ||
![]() |
b9437abefb | ||
![]() |
60d57d8530 | ||
![]() |
8c0bf94904 | ||
![]() |
42759f1cc4 | ||
![]() |
18fdeb483e | ||
![]() |
12c511b537 | ||
![]() |
d25ceef2d3 | ||
![]() |
8b48a1f024 | ||
![]() |
7430af5ffc | ||
![]() |
bc0f3aceb5 | ||
![]() |
9989f97091 | ||
![]() |
fc98b1bd70 | ||
![]() |
ab9cd664ec | ||
![]() |
13bfbdcbbc | ||
![]() |
70140f966a | ||
![]() |
8bd4afb235 | ||
![]() |
2a2864a306 | ||
![]() |
c3feafdb2c | ||
![]() |
86fb6c1dd2 | ||
![]() |
9f6af580cd | ||
![]() |
5bdcc5522a | ||
![]() |
c81429a870 | ||
![]() |
3d6e24eb73 | ||
![]() |
be598b0129 | ||
![]() |
ccc252965d | ||
![]() |
3beedc8d4d | ||
![]() |
74dc6c5894 | ||
![]() |
69526610f3 | ||
![]() |
921a0820f4 | ||
![]() |
2df56d2de2 | ||
![]() |
5b0c72a572 | ||
![]() |
c6cce54d5f | ||
![]() |
1f79648d57 | ||
![]() |
636400b75f | ||
![]() |
6f98076281 | ||
![]() |
1ed654c91b | ||
![]() |
c329e53811 | ||
![]() |
05e3a0d665 | ||
![]() |
4bd6314b3b | ||
![]() |
c5dae7039a | ||
![]() |
ca1f78173f | ||
![]() |
9ed6007b02 | ||
![]() |
4d696a29ef | ||
![]() |
45d64120ae | ||
![]() |
00a4d22dd9 | ||
![]() |
32c513f96e | ||
![]() |
7019cc2348 | ||
![]() |
a404cf0b39 | ||
![]() |
9649803dd6 | ||
![]() |
eecccac106 | ||
![]() |
f357adcd9a | ||
![]() |
e7998ced78 | ||
![]() |
8270988ed4 | ||
![]() |
2e2453f309 | ||
![]() |
bbb13454bf | ||
![]() |
3378822d2e | ||
![]() |
6e526b6f15 | ||
![]() |
5b1803cb05 | ||
![]() |
de5f760c37 | ||
![]() |
a33a46e8b8 | ||
![]() |
eb36830845 | ||
![]() |
3fd6dcee82 | ||
![]() |
55ce2bec5e | ||
![]() |
e02a1a7f28 | ||
![]() |
f9a3051328 | ||
![]() |
e4effc64ec | ||
![]() |
b3a361afee | ||
![]() |
a802ce772a | ||
![]() |
3a1966324d | ||
![]() |
819e575a28 | ||
![]() |
12bb597903 | ||
![]() |
7afe6c0ca7 | ||
![]() |
e1a27aa0a7 | ||
![]() |
f7f72c7e11 | ||
![]() |
e37b88dbda | ||
![]() |
d7303da85f | ||
![]() |
44024dbd02 | ||
![]() |
5a45a24055 | ||
![]() |
0a4db74f1f | ||
![]() |
f747252585 | ||
![]() |
7f00264e32 | ||
![]() |
d820cceeb6 | ||
![]() |
d4235bc33c | ||
![]() |
f21122b219 | ||
![]() |
01aaf476c1 | ||
![]() |
fec09b1543 | ||
![]() |
cf97f01245 | ||
![]() |
5ddd9a6bbf | ||
![]() |
07a60823ad | ||
![]() |
5ff62f0a8c | ||
![]() |
a84ee528aa | ||
![]() |
1aa4a0bd64 | ||
![]() |
37925b196b | ||
![]() |
f863b230dd | ||
![]() |
dcdc260f33 | ||
![]() |
2e98e380df | ||
![]() |
52557afa6a | ||
![]() |
f967630043 | ||
![]() |
9fb32f11be | ||
![]() |
64e0bba74c | ||
![]() |
ff950dd88b | ||
![]() |
9367bc3288 | ||
![]() |
d1ec945fe2 | ||
![]() |
0f28309344 | ||
![]() |
0263d52edf | ||
![]() |
127b106082 | ||
![]() |
6fd849fa50 | ||
![]() |
ca974ab801 | ||
![]() |
9b3d232503 | ||
![]() |
1ccb4bf732 | ||
![]() |
d484ee1ed0 | ||
![]() |
bb05934284 |
19
.github/workflows/tests.yml
vendored
Normal file
19
.github/workflows/tests.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_and_test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install packages and setup ldap/AD
|
||||||
|
run: sudo ./tests/test_env/deploy.sh
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: python3 setup.py test
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -54,3 +54,4 @@ coverage.xml
|
|||||||
# Sphinx documentation
|
# Sphinx documentation
|
||||||
docs/_build/
|
docs/_build/
|
||||||
|
|
||||||
|
ldapcherry-dev.ini
|
||||||
|
39
.travis.yml
39
.travis.yml
@ -1,31 +1,40 @@
|
|||||||
sudo: required
|
sudo: required
|
||||||
dist: trusty
|
dist: xenial
|
||||||
language: python
|
language: python
|
||||||
|
|
||||||
env:
|
|
||||||
- TRAVIS="yes"
|
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- '[ "$TEST_PEP8" == "1" ] || sudo ./tests/test_env/deploy.sh'
|
- '[ "$TEST_PEP8" == "1" ] || sudo ./tests/test_env/deploy.sh'
|
||||||
|
|
||||||
python:
|
|
||||||
- "2.7"
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- pip install -e .
|
- "pip install -e . -r $REQ_FILE"
|
||||||
- "if [[ $TEST_PEP8 == '1' ]]; then pip install pep8; fi"
|
- "if [[ $TEST_PEP8 == '1' ]]; then pip install pycodestyle; fi"
|
||||||
- pip install passlib
|
- pip install passlib
|
||||||
- pip install coveralls
|
- pip install coveralls
|
||||||
|
|
||||||
# command to run tests
|
script: "if [[ $TEST_PEP8 == '1' ]]; then pycodestyle --repeat --show-source --exclude=.venv,.tox,dist,docs,build,*.egg,tests,misc,setup.py .; else coverage run --source=ldapcherry setup.py test; fi"
|
||||||
#
|
|
||||||
#script:
|
|
||||||
# - coverage run --source=ldapcherry setup.py test
|
|
||||||
script: "if [[ $TEST_PEP8 == '1' ]]; then pep8 --repeat --show-source --exclude=.venv,.tox,dist,docs,build,*.egg,tests,misc . scripts/ldapcherryd; else coverage run --source=ldapcherry setup.py test; fi"
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- python: "2.7"
|
- python: "2.7"
|
||||||
env: TEST_PEP8=1
|
env:
|
||||||
|
TEST_PEP8=1
|
||||||
|
REQ_FILE=requirements.txt
|
||||||
|
- python: "2.7"
|
||||||
|
env:
|
||||||
|
TEST_PEP8=0
|
||||||
|
REQ_FILE=requirements-el7.txt
|
||||||
|
- python: "2.7"
|
||||||
|
env:
|
||||||
|
TEST_PEP8=0
|
||||||
|
REQ_FILE=requirements-stretch.txt
|
||||||
|
- python: "2.7"
|
||||||
|
env:
|
||||||
|
TEST_PEP8=0
|
||||||
|
REQ_FILE=requirements.txt
|
||||||
|
- python: "3.6"
|
||||||
|
env:
|
||||||
|
TEST_PEP8=0
|
||||||
|
REQ_FILE=requirements.txt
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- coveralls
|
- coveralls
|
||||||
after_failure:
|
after_failure:
|
||||||
|
@ -1,6 +1,69 @@
|
|||||||
Dev
|
Dev
|
||||||
***
|
***
|
||||||
|
|
||||||
|
Version 1.1.1
|
||||||
|
*************
|
||||||
|
|
||||||
|
* [fix ] fix double escaping issues introduced in 1.0.0
|
||||||
|
* [fix ] fix missing url escaping in links with querystring parameters (delete and modify page mostly)
|
||||||
|
* [fix ] fix log level not being honored in the backends
|
||||||
|
* [impr] clarify the role of 'key: True' of attributes.yml in the documentation
|
||||||
|
* [impr] add a few more comments in the .ini file to explain better the \*_filter_tmpl and group_attr parameters
|
||||||
|
* [impr] add debug log to help debug ldap search filters
|
||||||
|
|
||||||
|
Version 1.1.0
|
||||||
|
*************
|
||||||
|
|
||||||
|
* [feat] add stdout as a valid log method (useful when running with docker)
|
||||||
|
|
||||||
|
Version 1.0.1
|
||||||
|
*************
|
||||||
|
|
||||||
|
* [fix ] fix error handling when adding user that already exists
|
||||||
|
|
||||||
|
Version 1.0.0
|
||||||
|
*************
|
||||||
|
|
||||||
|
* [sec ] fix XSS injection in the url redirect in the login page (thanks to jthiltges)
|
||||||
|
* [fix ] fix configuration consistency check for attribute file (error if a given backend is not declared in main .ini file but in attributes)
|
||||||
|
* [fix ] remove a few deprecation warnings
|
||||||
|
* [fix ] fix potential issue with group names containing non-ascii characters
|
||||||
|
* [feat] support for python 3
|
||||||
|
* [feat] support for python-ldap 3.X.X
|
||||||
|
* [impr] better log error message if inconsistency between role, attribute and main .ini file for backends
|
||||||
|
* [impr] more systematic use of html and url escaping in the html rendering to prevent against content injection (thanks to jthiltges)
|
||||||
|
* [impr] more testing for various versions of python and python-ldap
|
||||||
|
|
||||||
|
Version 0.5.2
|
||||||
|
*************
|
||||||
|
|
||||||
|
* [fix ] regression in 0.5.1, setup.py could not work without the dependencies already installed
|
||||||
|
|
||||||
|
Version 0.5.1
|
||||||
|
*************
|
||||||
|
|
||||||
|
* [impr] cleaner align of labels (input-group-addon width)
|
||||||
|
|
||||||
|
Version 0.5.0
|
||||||
|
*************
|
||||||
|
|
||||||
|
* [feat] add handling of textfielf (thanks to Stan Rudenko)
|
||||||
|
* [fix ] fix ldap.group_attr.<attr> handling with attr different than dn (uid for example)
|
||||||
|
* [impr] removing duplicate option in form select fields
|
||||||
|
* [impr] add dynamic resizing to align form labels (input-group-addon width)
|
||||||
|
|
||||||
|
Version 0.4.0
|
||||||
|
*************
|
||||||
|
|
||||||
|
* [impr] add unit test for multi backend setup
|
||||||
|
* [fix ] notify on add in case if user is already in one backend
|
||||||
|
* [fix ] notify on modify in case if user is not in every backend
|
||||||
|
* [fix ] delete user in all backends even if it doesn't exist in one of them
|
||||||
|
* [fix ] fix bad handling of = or & in passwords in ppolicy checker (js)
|
||||||
|
* [fix ] fix many encoding errors in AD backend
|
||||||
|
* [impr] add unit tests on AD backend
|
||||||
|
* [impr] display the admin result page if searching as admin in navbar form
|
||||||
|
|
||||||
Version 0.3.5
|
Version 0.3.5
|
||||||
*************
|
*************
|
||||||
|
|
||||||
|
29
README.rst
29
README.rst
@ -6,15 +6,9 @@
|
|||||||
|
|
||||||
Nice and simple application to manage users and groups in multiple directory services.
|
Nice and simple application to manage users and groups in multiple directory services.
|
||||||
|
|
||||||
.. image:: https://travis-ci.org/kakwa/ldapcherry.svg?branch=master
|
.. image:: https://github.com/kakwa/ldapcherry/actions/workflows/tests.yml/badge.svg
|
||||||
:target: https://travis-ci.org/kakwa/ldapcherry
|
:target: https://github.com/kakwa/ldapcherry/actions/workflows/tests.yml
|
||||||
|
:alt: CI
|
||||||
.. image:: https://coveralls.io/repos/kakwa/ldapcherry/badge.svg
|
|
||||||
:target: https://coveralls.io/r/kakwa/ldapcherry
|
|
||||||
|
|
||||||
.. image:: https://img.shields.io/pypi/dm/ldapcherry.svg
|
|
||||||
:target: https://pypi.python.org/pypi/ldapcherry
|
|
||||||
:alt: Number of PyPI downloads
|
|
||||||
|
|
||||||
.. image:: https://img.shields.io/pypi/v/ldapcherry.svg
|
.. image:: https://img.shields.io/pypi/v/ldapcherry.svg
|
||||||
:target: https://pypi.python.org/pypi/ldapcherry
|
:target: https://pypi.python.org/pypi/ldapcherry
|
||||||
@ -40,7 +34,7 @@ Nice and simple application to manage users and groups in multiple directory ser
|
|||||||
|
|
||||||
LdapCherry is a CherryPY application to manage users and groups in multiple directory services.
|
LdapCherry is a CherryPY application to manage users and groups in multiple directory services.
|
||||||
|
|
||||||
It's main features are:
|
Its main features are:
|
||||||
|
|
||||||
* manage multiple directories/databases backends in an unified way
|
* manage multiple directories/databases backends in an unified way
|
||||||
* roles management (as in "groups of groups")
|
* roles management (as in "groups of groups")
|
||||||
@ -70,11 +64,16 @@ The default backend plugins permit to manage Ldap and Active Directory.
|
|||||||
|
|
||||||
.. sourcecode:: bash
|
.. sourcecode:: bash
|
||||||
|
|
||||||
|
# clone the repository
|
||||||
|
$ git clone https://github.com/kakwa/ldapcherry && cd ldapcherry
|
||||||
|
|
||||||
# change the directory where to put the configuration (default: /etc)
|
# change the directory where to put the configuration (default: /etc)
|
||||||
$ export SYSCONFDIR=<sys conf dir>
|
$ export SYSCONFDIR=/etc
|
||||||
|
# change the directory where to put the resource (default: /usr/share)
|
||||||
|
$ export DATAROOTDIR=/usr/share/
|
||||||
|
|
||||||
# install ldapcherry
|
# install ldapcherry
|
||||||
$ pip install ldapcherry
|
$ python setup.py install
|
||||||
|
|
||||||
# edit configuration files
|
# edit configuration files
|
||||||
$ vi /etc/ldapcherry/ldapcherry.ini
|
$ vi /etc/ldapcherry/ldapcherry.ini
|
||||||
@ -82,9 +81,11 @@ The default backend plugins permit to manage Ldap and Active Directory.
|
|||||||
$ vi /etc/ldapcherry/attributes.yml
|
$ vi /etc/ldapcherry/attributes.yml
|
||||||
|
|
||||||
# launch ldapcherry
|
# launch ldapcherry
|
||||||
$ ldapcherryd -c /etc/ldapcherry/ldapcherry.ini
|
$ ldapcherryd -c /etc/ldapcherry/ldapcherry.ini -D
|
||||||
|
|
||||||
|
|
||||||
|
Debian and RPM packages are also available here: `https://github.com/kakwa/kakwalab-pkg` (package name ``ldapcherry``).
|
||||||
|
|
||||||
***********
|
***********
|
||||||
License
|
License
|
||||||
***********
|
***********
|
||||||
@ -95,7 +96,7 @@ LdapCherry is published under the MIT Public License.
|
|||||||
Discussion / Help / Updates
|
Discussion / Help / Updates
|
||||||
*******************************
|
*******************************
|
||||||
|
|
||||||
* IRC: `Freenode <http://freenode.net/>`_ ``#ldapcherry`` channel
|
* IRC: `Libera <https://libera.chat/>`_ ``#ldapcherry`` channel
|
||||||
* Bugtracker: `Github <https://github.com/kakwa/ldapcherry/issues>`_
|
* Bugtracker: `Github <https://github.com/kakwa/ldapcherry/issues>`_
|
||||||
|
|
||||||
----
|
----
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
server.socket_host = '127.0.0.1'
|
server.socket_host = '127.0.0.1'
|
||||||
# port
|
# port
|
||||||
server.socket_port = 8080
|
server.socket_port = 8080
|
||||||
|
|
||||||
|
# it's also possible to run bound to a unix socket
|
||||||
|
#server.socket_file = '/tmp/lc.sock'
|
||||||
|
|
||||||
# number of threads
|
# number of threads
|
||||||
server.thread_pool = 8
|
server.thread_pool = 8
|
||||||
#don't show traceback on error
|
#don't show traceback on error
|
||||||
@ -24,6 +28,14 @@ request.show_tracebacks = False
|
|||||||
## error and ldapcherry log file
|
## error and ldapcherry log file
|
||||||
#log.error_file = '/tmp/ldapcherry_error.log'
|
#log.error_file = '/tmp/ldapcherry_error.log'
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
# configuration to log to stdout #
|
||||||
|
#####################################
|
||||||
|
## logger stdout for access log
|
||||||
|
#log.access_handler = 'stdout'
|
||||||
|
## logger stdout for error and ldapcherry log
|
||||||
|
#log.error_handler = 'stdout'
|
||||||
|
|
||||||
#####################################
|
#####################################
|
||||||
# configuration to log in syslog #
|
# configuration to log in syslog #
|
||||||
#####################################
|
#####################################
|
||||||
@ -94,16 +106,24 @@ ldap.timeout = 1
|
|||||||
ldap.groupdn = 'ou=group,dc=example,dc=org'
|
ldap.groupdn = 'ou=group,dc=example,dc=org'
|
||||||
# users dn
|
# users dn
|
||||||
ldap.userdn = 'ou=people,dc=example,dc=org'
|
ldap.userdn = 'ou=people,dc=example,dc=org'
|
||||||
# ldapsearch filter to get a user
|
|
||||||
|
# ldapsearch filter to get one specific user
|
||||||
|
# %(username)s is content of the attribute marked 'key: True' in the attributes.file config file
|
||||||
ldap.user_filter_tmpl = '(uid=%(username)s)'
|
ldap.user_filter_tmpl = '(uid=%(username)s)'
|
||||||
# ldapsearch filter to get groups of a user
|
# ldapsearch filter to get groups of a user
|
||||||
|
# %(username)s is content of the attribute marked 'key: True' in the attributes.file config file
|
||||||
ldap.group_filter_tmpl = '(member=uid=%(username)s,ou=People,dc=example,dc=org)'
|
ldap.group_filter_tmpl = '(member=uid=%(username)s,ou=People,dc=example,dc=org)'
|
||||||
# filter to search users
|
# filter to search users
|
||||||
|
# %(searchstring)s is the content passed through the search box
|
||||||
ldap.search_filter_tmpl = '(|(uid=%(searchstring)s*)(sn=%(searchstring)s*))'
|
ldap.search_filter_tmpl = '(|(uid=%(searchstring)s*)(sn=%(searchstring)s*))'
|
||||||
|
|
||||||
# ldap group attributes and how to fill them
|
# ldap group attributes and how to fill them
|
||||||
|
# 'member' is the name of the attribute
|
||||||
|
# for the template, any of the user's ldap attributes can be user
|
||||||
ldap.group_attr.member = "%(dn)s"
|
ldap.group_attr.member = "%(dn)s"
|
||||||
|
# same with memverUid and the uid user's attribute
|
||||||
#ldap.group_attr.memberUid = "%(uid)s"
|
#ldap.group_attr.memberUid = "%(uid)s"
|
||||||
|
|
||||||
# object classes of a user entry
|
# object classes of a user entry
|
||||||
ldap.objectclasses = 'top, person, posixAccount, inetOrgPerson'
|
ldap.objectclasses = 'top, person, posixAccount, inetOrgPerson'
|
||||||
# dn entry attribute for an ldap user
|
# dn entry attribute for an ldap user
|
||||||
|
@ -25,12 +25,12 @@ admin-lv2:
|
|||||||
# ad:
|
# ad:
|
||||||
# - Administrators
|
# - Administrators
|
||||||
|
|
||||||
developpers:
|
developers:
|
||||||
display_name: Developpers
|
display_name: Developpers
|
||||||
description: Developpers of the system
|
description: Developpers of the system
|
||||||
backends_groups:
|
backends_groups:
|
||||||
ldap:
|
ldap:
|
||||||
- cn=developpers,ou=Group,dc=example,dc=org
|
- cn=developers,ou=Group,dc=example,dc=org
|
||||||
- cn=users,ou=Group,dc=example,dc=org
|
- cn=users,ou=Group,dc=example,dc=org
|
||||||
|
|
||||||
users:
|
users:
|
||||||
|
@ -47,48 +47,55 @@ Configuration
|
|||||||
|
|
||||||
The ldap backend exposes the following parameters:
|
The ldap backend exposes the following parameters:
|
||||||
|
|
||||||
+--------------------------+----------+------------------------------------+--------------------------+--------------------------------------------+
|
+--------------------------+----------+------------------------------------+--------------------------+------------------------------------------------+
|
||||||
| Parameter | Section | Description | Values | Comment |
|
| Parameter | Section | Description | Values | Comment |
|
||||||
+==========================+==========+====================================+==========================+============================================+
|
+==========================+==========+====================================+==========================+================================================+
|
||||||
| uri | backends | The ldap uri to access | ldap uri | * use ldap:// for clear/starttls |
|
| uri | backends | The ldap uri to access | ldap uri | * use ldap:// for clear/starttls |
|
||||||
| | | | | * use ldaps:// for ssl |
|
| | | | | * use ldaps:// for ssl |
|
||||||
| | | | | * custom port: ldap://<host>:<port> |
|
| | | | | * custom port: ldap://<host>:<port> |
|
||||||
+--------------------------+----------+------------------------------------+--------------------------+--------------------------------------------+
|
+--------------------------+----------+------------------------------------+--------------------------+------------------------------------------------+
|
||||||
| ca | backends | Path to the CA file | file path | optional |
|
| ca | backends | Path to the CA file | file path | optional |
|
||||||
+--------------------------+----------+------------------------------------+--------------------------+--------------------------------------------+
|
+--------------------------+----------+------------------------------------+--------------------------+------------------------------------------------+
|
||||||
| starttls | backends | Use starttls | 'on' or 'off' | optional |
|
| starttls | backends | Use starttls | 'on' or 'off' | optional |
|
||||||
+--------------------------+----------+------------------------------------+--------------------------+--------------------------------------------+
|
+--------------------------+----------+------------------------------------+--------------------------+------------------------------------------------+
|
||||||
| checkcert | backends | Check the server certificat | 'on' or 'off' | optional |
|
| checkcert | backends | Check the server certificat | 'on' or 'off' | optional |
|
||||||
+--------------------------+----------+------------------------------------+--------------------------+--------------------------------------------+
|
+--------------------------+----------+------------------------------------+--------------------------+------------------------------------------------+
|
||||||
| binddn | backends | The bind dn to use | ldap dn | This dn must have read/write permissions |
|
| binddn | backends | The bind dn to use | ldap dn | This dn must have read/write permissions |
|
||||||
+--------------------------+----------+------------------------------------+--------------------------+--------------------------------------------+
|
+--------------------------+----------+------------------------------------+--------------------------+------------------------------------------------+
|
||||||
| password | backends | The password of the bind dn | password | |
|
| password | backends | The password of the bind dn | password | |
|
||||||
+--------------------------+----------+------------------------------------+--------------------------+--------------------------------------------+
|
+--------------------------+----------+------------------------------------+--------------------------+------------------------------------------------+
|
||||||
| timeout | backends | Ldap connexion timeout | integer (second) | |
|
| timeout | backends | Ldap connexion timeout | integer (second) | |
|
||||||
+--------------------------+----------+------------------------------------+--------------------------+--------------------------------------------+
|
+--------------------------+----------+------------------------------------+--------------------------+------------------------------------------------+
|
||||||
| password | backends | The password of the bind dn | password | |
|
| password | backends | The password of the bind dn | password | |
|
||||||
+--------------------------+----------+------------------------------------+--------------------------+--------------------------------------------+
|
+--------------------------+----------+------------------------------------+--------------------------+------------------------------------------------+
|
||||||
| groupdn | backends | The ldap dn where groups are | ldap dn | |
|
| groupdn | backends | The ldap dn where groups are | ldap dn | |
|
||||||
+--------------------------+----------+------------------------------------+--------------------------+--------------------------------------------+
|
+--------------------------+----------+------------------------------------+--------------------------+------------------------------------------------+
|
||||||
| userdn | backends | The ldap dn where users are | ldap dn | |
|
| userdn | backends | The ldap dn where users are | ldap dn | |
|
||||||
+--------------------------+----------+------------------------------------+--------------------------+--------------------------------------------+
|
+--------------------------+----------+------------------------------------+--------------------------+------------------------------------------------+
|
||||||
| user_filter_tmpl | backends | The search filter template | ldap search filter | The user identifier is passed through |
|
| user_filter_tmpl | backends | The search filter template | ldap search filter | The user identifier is passed through |
|
||||||
| | | to recover a given user | template | the **username** variable (*%(username)s*).|
|
| | | to recover a given user | template | the **username** variable (*%(username)s*) |
|
||||||
+--------------------------+----------+------------------------------------+--------------------------+--------------------------------------------+
|
| | | | | |
|
||||||
| group_filter_tmpl | backends | The search filter template to | ldap search filter | The following variables are usable: |
|
| | | | | **username** is the content of the |
|
||||||
| | | recover the groups of a given user | template | * **username**: the user key attribute |
|
| | | | | the attribute marked by '**key: True**' |
|
||||||
| | | | | * **userdn**: the user ldap dn |
|
| | | | | in the **attributes.yml** file |
|
||||||
+--------------------------+----------+------------------------------------+--------------------------+--------------------------------------------+
|
+--------------------------+----------+------------------------------------+--------------------------+------------------------------------------------+
|
||||||
| group_attr.<member attr> | backends | Member attribute template value | template | * <member attr> is the member attribute |
|
| group_filter_tmpl | backends | The search filter template to | ldap search filter | The following variables are usable: |
|
||||||
| | | | | in groups dn entries |
|
| | | recover the groups of a given user | template | |
|
||||||
| | | | | * every user attributes are exposed |
|
| | | recover the groups of a given user | template | * **username**: the user's key attribute |
|
||||||
| | | | | in the template |
|
| | | | | * **userdn**: the user's ldap dn |
|
||||||
| | | | | * multiple attributes can be set |
|
+--------------------------+----------+------------------------------------+--------------------------+------------------------------------------------+
|
||||||
+--------------------------+----------+------------------------------------+--------------------------+--------------------------------------------+
|
| group_attr.<member attr> | backends | Member attribute template value | template | * <member attr> is the member attribute |
|
||||||
| objectclasses | backends | list of object classes for users | comma separated list | |
|
| | | | | in groups dn entries |
|
||||||
+--------------------------+----------+------------------------------------+--------------------------+--------------------------------------------+
|
| | | | | * every user attributes are exposed |
|
||||||
| dn_user_attr | backends | attribute used in users dn | dn attribute | |
|
| | | | | in the template |
|
||||||
+--------------------------+----------+------------------------------------+--------------------------+--------------------------------------------+
|
| | | | | * multiple <memver attr> attributes |
|
||||||
|
| | | | | can be set (ex: group_attr.member |
|
||||||
|
| | | | | (ex: group_attr.member, group_attr.usermemb) |
|
||||||
|
+--------------------------+----------+------------------------------------+--------------------------+------------------------------------------------+
|
||||||
|
| objectclasses | backends | list of object classes for users | comma separated list | |
|
||||||
|
+--------------------------+----------+------------------------------------+--------------------------+------------------------------------------------+
|
||||||
|
| dn_user_attr | backends | attribute used in users dn | dn attribute | |
|
||||||
|
+--------------------------+----------+------------------------------------+--------------------------+------------------------------------------------+
|
||||||
|
|
||||||
|
|
||||||
Example
|
Example
|
||||||
@ -96,46 +103,58 @@ Example
|
|||||||
|
|
||||||
.. sourcecode:: ini
|
.. sourcecode:: ini
|
||||||
|
|
||||||
[backends]
|
[backends]
|
||||||
|
|
||||||
# name of the module
|
#####################################
|
||||||
ldap.module = 'ldapcherry.backend.backendLdap'
|
# configuration of ldap backend #
|
||||||
# display name of the ldap
|
#####################################
|
||||||
ldap.display_name = 'My Ldap Directory'
|
|
||||||
|
# name of the module
|
||||||
# uri of the ldap directory
|
ldap.module = 'ldapcherry.backend.backendLdap'
|
||||||
ldap.uri = 'ldap://ldap.ldapcherry.org'
|
# display name of the ldap
|
||||||
# ca to use for ssl/tls connexion
|
ldap.display_name = 'My Ldap Directory'
|
||||||
#ldap.ca = '/etc/dnscherry/TEST-cacert.pem'
|
|
||||||
# use start tls
|
# uri of the ldap directory
|
||||||
#ldap.starttls = 'off'
|
ldap.uri = 'ldap://ldap.ldapcherry.org'
|
||||||
# check server certificate (for tls)
|
# ca to use for ssl/tls connexion
|
||||||
#ldap.checkcert = 'off'
|
#ldap.ca = '/etc/dnscherry/TEST-cacert.pem'
|
||||||
# bind dn to the ldap
|
# use start tls
|
||||||
ldap.binddn = 'cn=dnscherry,dc=example,dc=org'
|
#ldap.starttls = 'off'
|
||||||
# password of the bind dn
|
# check server certificate (for tls)
|
||||||
ldap.password = 'password'
|
#ldap.checkcert = 'off'
|
||||||
# timeout of ldap connexion (in second)
|
# bind dn to the ldap
|
||||||
ldap.timeout = 1
|
ldap.binddn = 'cn=dnscherry,dc=example,dc=org'
|
||||||
|
# password of the bind dn
|
||||||
# groups dn
|
ldap.password = 'password'
|
||||||
ldap.groupdn = 'ou=group,dc=example,dc=org'
|
# timeout of ldap connexion (in second)
|
||||||
# users dn
|
ldap.timeout = 1
|
||||||
ldap.userdn = 'ou=people,dc=example,dc=org'
|
|
||||||
# ldapsearch filter to get a user
|
# groups dn
|
||||||
ldap.user_filter_tmpl = '(uid=%(username)s)'
|
ldap.groupdn = 'ou=group,dc=example,dc=org'
|
||||||
# ldapsearch filter to get groups of a user
|
# users dn
|
||||||
ldap.group_filter_tmpl = '(member=uid=%(username)s,ou=People,dc=example,dc=org)'
|
ldap.userdn = 'ou=people,dc=example,dc=org'
|
||||||
# filter to search users
|
|
||||||
ldap.search_filter_tmpl = '(|(uid=%(searchstring)s*)(sn=%(searchstring)s*))'
|
# ldapsearch filter to get one specific user
|
||||||
|
# %(username)s is content of the attribute marked 'key: True' in the attributes.file config file
|
||||||
# ldap group attributes and how to fill them
|
ldap.user_filter_tmpl = '(uid=%(username)s)'
|
||||||
ldap.group_attr.member = "%(dn)s"
|
# ldapsearch filter to get groups of a user
|
||||||
#ldap.group_attr.memberUid = "%(uid)s"
|
# %(username)s is content of the attribute marked 'key: True' in the attributes.file config file
|
||||||
# object classes of a user entry
|
ldap.group_filter_tmpl = '(member=uid=%(username)s,ou=People,dc=example,dc=org)'
|
||||||
ldap.objectclasses = 'top, person, posixAccount, inetOrgPerson'
|
# filter to search users
|
||||||
# dn entry attribute for an ldap user
|
# %(searchstring)s is the content passed through the search box
|
||||||
ldap.dn_user_attr = 'uid'
|
ldap.search_filter_tmpl = '(|(uid=%(searchstring)s*)(sn=%(searchstring)s*))'
|
||||||
|
|
||||||
|
# ldap group attributes and how to fill them
|
||||||
|
# 'member' is the name of the attribute
|
||||||
|
# for the template, any of the user's ldap attributes can be user
|
||||||
|
ldap.group_attr.member = "%(dn)s"
|
||||||
|
# same with memverUid and the uid user's attribute
|
||||||
|
#ldap.group_attr.memberUid = "%(uid)s"
|
||||||
|
|
||||||
|
# object classes of a user entry
|
||||||
|
ldap.objectclasses = 'top, person, posixAccount, inetOrgPerson'
|
||||||
|
# dn entry attribute for an ldap user
|
||||||
|
ldap.dn_user_attr = 'uid'
|
||||||
|
|
||||||
|
|
||||||
Active Directory Backend
|
Active Directory Backend
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import sys
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from mock import Mock as MagicMock
|
from mock import Mock as MagicMock
|
||||||
@ -32,6 +31,9 @@ MOCK_MODULES = ['ldap']
|
|||||||
sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES)
|
sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES)
|
||||||
|
|
||||||
sys.path.insert(0, os.path.abspath('..'))
|
sys.path.insert(0, os.path.abspath('..'))
|
||||||
|
sys.path.insert(0, os.path.abspath('../ldapcherry'))
|
||||||
|
|
||||||
|
from version import version
|
||||||
|
|
||||||
# -- General configuration -----------------------------------------------------
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
@ -64,7 +66,7 @@ copyright = u'2016, Pierre-Francois Carpentier'
|
|||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = '0.3.5'
|
release = version
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
|
@ -62,7 +62,7 @@ The mandatory parameters for an attribute, and their format are the following:
|
|||||||
description: <Human readable description of the attribute> # (free text)
|
description: <Human readable description of the attribute> # (free text)
|
||||||
display_name: <Display name in LdapCherry forms> # (free text)
|
display_name: <Display name in LdapCherry forms> # (free text)
|
||||||
weight: <weight controlling the display order of the attributes, lower is first> # (integer)
|
weight: <weight controlling the display order of the attributes, lower is first> # (integer)
|
||||||
type: <type of the attributes> # (in ['int', 'string', 'email', 'stringlist', 'fix'])
|
type: <type of the attributes> # (in ['int', 'string', 'email', 'stringlist', 'fix', 'textfield'])
|
||||||
backends: # (list of backend attributes name)
|
backends: # (list of backend attributes name)
|
||||||
- <backend id 1>: <backend 1 attribute name>
|
- <backend id 1>: <backend 1 attribute name>
|
||||||
- <backend id 2>: <backend 2 attribute name>
|
- <backend id 2>: <backend 2 attribute name>
|
||||||
@ -76,6 +76,18 @@ The mandatory parameters for an attribute, and their format are the following:
|
|||||||
<backend id> (the backend id) must be defined in main ini configuration file.
|
<backend id> (the backend id) must be defined in main ini configuration file.
|
||||||
LdapCherry won't start if it's not.
|
LdapCherry won't start if it's not.
|
||||||
|
|
||||||
|
Type listing
|
||||||
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The following **type** are supported:
|
||||||
|
|
||||||
|
* **int**: an integer (ex: uid)
|
||||||
|
* **string**: a string (ex: first name)
|
||||||
|
* **stringlist**: a string to choose from a given list of strings (ex: one of /bin/sh, /bin/bash /bin/zsh for a shell)
|
||||||
|
* **textfield**: free multiline text (ex: an SSH key)
|
||||||
|
* **email**: an email address
|
||||||
|
* **fix**: a fix value, only present shown information purposes
|
||||||
|
|
||||||
Type stringlist values
|
Type stringlist values
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
@ -100,7 +112,12 @@ If **type** is set to **stringlist** the parameter **values** must be filled wit
|
|||||||
Key attribute:
|
Key attribute:
|
||||||
^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^
|
||||||
|
|
||||||
One attribute must be used as a unique key across all backends:
|
One attribute must be used as a unique key across all backends.
|
||||||
|
|
||||||
|
It acts as a reconciliation key.
|
||||||
|
|
||||||
|
It also marks which attribute must be used within ldapcherry (ex: querysting parameter in links)
|
||||||
|
to point to one given user.
|
||||||
|
|
||||||
To set the key attribute, you must set **key** to **True** on this attribute.
|
To set the key attribute, you must set **key** to **True** on this attribute.
|
||||||
|
|
||||||
@ -433,16 +450,16 @@ Logging
|
|||||||
|
|
||||||
LdapCherry has two loggers, one for errors and applicative actions (login, del/add, logout...) and one for access logs.
|
LdapCherry has two loggers, one for errors and applicative actions (login, del/add, logout...) and one for access logs.
|
||||||
|
|
||||||
Each logger can be configured to log to syslog, file or be disabled.
|
Each logger can be configured to log to **syslog**, **file**, **stdout** or be disabled.
|
||||||
|
|
||||||
Logging parameters:
|
Logging parameters:
|
||||||
|
|
||||||
+--------------------+---------+---------------------------------+-------------------------------------------------+----------------------------------------+
|
+--------------------+---------+---------------------------------+-------------------------------------------------+----------------------------------------+
|
||||||
| Parameter | Section | Description | Values | Comment |
|
| Parameter | Section | Description | Values | Comment |
|
||||||
+====================+=========+=================================+=================================================+========================================+
|
+====================+=========+=================================+=================================================+========================================+
|
||||||
| log.access_handler | global | Logger type for access log | 'syslog', 'file', 'none' | |
|
| log.access_handler | global | Logger type for access log | 'syslog', 'file', 'stdout', 'none' | |
|
||||||
+--------------------+---------+---------------------------------+-------------------------------------------------+----------------------------------------+
|
+--------------------+---------+---------------------------------+-------------------------------------------------+----------------------------------------+
|
||||||
| log.error_handler | global | Logger type for applicative log | 'syslog', 'file', 'none' | |
|
| log.error_handler | global | Logger type for applicative log | 'syslog', 'file', 'stdout', 'none' | |
|
||||||
+--------------------+---------+---------------------------------+-------------------------------------------------+----------------------------------------+
|
+--------------------+---------+---------------------------------+-------------------------------------------------+----------------------------------------+
|
||||||
| log.access_file | global | log file for access log | path to log file | only used if log.access_handler='file' |
|
| log.access_file | global | log file for access log | path to log file | only used if log.access_handler='file' |
|
||||||
+--------------------+---------+---------------------------------+-------------------------------------------------+----------------------------------------+
|
+--------------------+---------+---------------------------------+-------------------------------------------------+----------------------------------------+
|
||||||
@ -465,6 +482,14 @@ Example:
|
|||||||
log.level = 'info'
|
log.level = 'info'
|
||||||
|
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
'debug' should not be used in production.
|
||||||
|
|
||||||
|
It tends to log a lot.
|
||||||
|
More significantly can represent a security issue,
|
||||||
|
as things like passwords will be logged 'clear text'.
|
||||||
|
|
||||||
Custom javascript
|
Custom javascript
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -18,17 +18,49 @@ This init script is available in **goodies/init-debian**.
|
|||||||
Apache Vhost
|
Apache Vhost
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
Basic Apache Vhost:
|
||||||
|
|
||||||
.. literalinclude:: ../goodies/apache.conf
|
.. literalinclude:: ../goodies/apache.conf
|
||||||
:language: xml
|
:language: xml
|
||||||
|
|
||||||
Nginx Vhost
|
Nginx Vhost
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
Basic Nginx Vhost:
|
||||||
|
|
||||||
.. literalinclude:: ../goodies/nginx.conf
|
.. literalinclude:: ../goodies/nginx.conf
|
||||||
:language: yaml
|
:language: yaml
|
||||||
|
|
||||||
|
Nginx Vhost (FastCGI)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Nginx Vhost in FastCGI mode:
|
||||||
|
|
||||||
|
.. literalinclude:: ../goodies/nginx-fastcgi.conf
|
||||||
|
:language: yaml
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
LdapCherry requires the python flup module to run in FastCGI
|
||||||
|
|
||||||
Lighttpd Vhost
|
Lighttpd Vhost
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
Basic Lighttpd Vhost
|
||||||
|
|
||||||
.. literalinclude:: ../goodies/lighttpd.conf
|
.. literalinclude:: ../goodies/lighttpd.conf
|
||||||
:language: yaml
|
:language: yaml
|
||||||
|
|
||||||
|
Demo Backend Configuration Files
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
The files here are the ones that are used at the demo site at `ldapcherry.kakwalab.ovh <https://ldapcherry.kakwalab.ovh/>`_ and can be used for a self-hosted demo backend:
|
||||||
|
|
||||||
|
.. literalinclude:: ../goodies/demo_backend_configs/attributes.yml
|
||||||
|
:language: yaml
|
||||||
|
|
||||||
|
.. literalinclude:: ../goodies/demo_backend_configs/roles.yml
|
||||||
|
:language: yaml
|
||||||
|
|
||||||
|
.. literalinclude:: ../goodies/demo_backend_configs/ldapcherry.ini
|
||||||
|
:language: ini
|
||||||
|
@ -12,18 +12,13 @@ Download the latest release from `GitHub <https://github.com/kakwa/ldapcherry/re
|
|||||||
$ cd ldapcherry*
|
$ cd ldapcherry*
|
||||||
$ python setup.py install
|
$ python setup.py install
|
||||||
|
|
||||||
From Pypi
|
Alternatively, you can install from git:
|
||||||
---------
|
|
||||||
|
|
||||||
.. sourcecode:: bash
|
|
||||||
|
|
||||||
$ pip install ldapcherry
|
|
||||||
|
|
||||||
or
|
|
||||||
|
|
||||||
.. sourcecode:: bash
|
.. sourcecode:: bash
|
||||||
|
|
||||||
$ easy_install ldapcherry
|
$ git clone https://github.com/kakwa/ldapcherry
|
||||||
|
$ cd ldapcherry
|
||||||
|
$ python setup.py install
|
||||||
|
|
||||||
Installed files
|
Installed files
|
||||||
---------------
|
---------------
|
||||||
@ -38,8 +33,12 @@ These directories can be changed by exporting the following variables before lau
|
|||||||
|
|
||||||
.. sourcecode:: bash
|
.. sourcecode:: bash
|
||||||
|
|
||||||
#optional, default sys.prefix + 'share' (/usr/share/ on most Linux)
|
# optional, default sys.prefix + 'share' (/usr/share/ on most Linux)
|
||||||
$ export DATAROOTDIR=/usr/local/share/
|
$ export DATAROOTDIR=/usr/local/share/
|
||||||
#optional, default /etc/
|
|
||||||
|
# optional, default /etc/
|
||||||
$ export SYSCONFDIR=/usr/local/etc/
|
$ export SYSCONFDIR=/usr/local/etc/
|
||||||
|
|
||||||
|
.. note:: if --root is passed, the install prefix is honored for these directories
|
||||||
|
|
||||||
|
.. warning:: If you change these directories, **templates.dir** and **tools.staticdir.dir** in *ldapcherry.ini* need to be modified accordingly.
|
||||||
|
112
goodies/demo_backend_configs/attributes.yml
Normal file
112
goodies/demo_backend_configs/attributes.yml
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
description: "First Name and Display Name"
|
||||||
|
display_name: "Display Name"
|
||||||
|
type: string
|
||||||
|
weight: 30
|
||||||
|
autofill:
|
||||||
|
function: lcDisplayName
|
||||||
|
args:
|
||||||
|
- $first-name
|
||||||
|
- $name
|
||||||
|
backends:
|
||||||
|
demo: cn
|
||||||
|
first-name:
|
||||||
|
description: "First name of the user"
|
||||||
|
display_name: "First Name"
|
||||||
|
search_displayed: True
|
||||||
|
type: string
|
||||||
|
weight: 20
|
||||||
|
backends:
|
||||||
|
demo: givenName
|
||||||
|
name:
|
||||||
|
description: "Family name of the user"
|
||||||
|
display_name: "Name"
|
||||||
|
search_displayed: True
|
||||||
|
weight: 10
|
||||||
|
type: string
|
||||||
|
backends:
|
||||||
|
demo: sn
|
||||||
|
email:
|
||||||
|
description: "Email of the user"
|
||||||
|
display_name: "Email"
|
||||||
|
search_displayed: True
|
||||||
|
type: email
|
||||||
|
weight: 40
|
||||||
|
autofill:
|
||||||
|
function: lcMail
|
||||||
|
args:
|
||||||
|
- $first-name
|
||||||
|
- $name
|
||||||
|
- '@example.com'
|
||||||
|
backends:
|
||||||
|
demo: mail
|
||||||
|
uid:
|
||||||
|
description: "UID of the user"
|
||||||
|
display_name: "UID"
|
||||||
|
search_displayed: True
|
||||||
|
key: True
|
||||||
|
type: string
|
||||||
|
weight: 50
|
||||||
|
autofill:
|
||||||
|
function: lcUid
|
||||||
|
args:
|
||||||
|
- $first-name
|
||||||
|
- $name
|
||||||
|
- '10000'
|
||||||
|
- '40000'
|
||||||
|
backends:
|
||||||
|
demo: uid
|
||||||
|
uidNumber:
|
||||||
|
description: "User ID Number of the user"
|
||||||
|
display_name: "UID Number"
|
||||||
|
weight: 60
|
||||||
|
type: int
|
||||||
|
autofill:
|
||||||
|
function: lcUidNumber
|
||||||
|
args:
|
||||||
|
- $first-name
|
||||||
|
- $name
|
||||||
|
- '10000'
|
||||||
|
- '40000'
|
||||||
|
backends:
|
||||||
|
demo: uidNumber
|
||||||
|
gidNumber:
|
||||||
|
description: "Group ID Number of the user"
|
||||||
|
display_name: "GID Number"
|
||||||
|
weight: 70
|
||||||
|
type: int
|
||||||
|
default: '10000'
|
||||||
|
backends:
|
||||||
|
demo: gidNumber
|
||||||
|
shell:
|
||||||
|
description: "Shell of the user"
|
||||||
|
display_name: "Shell"
|
||||||
|
weight: 80
|
||||||
|
self: True
|
||||||
|
type: stringlist
|
||||||
|
values:
|
||||||
|
- /bin/bash
|
||||||
|
- /bin/zsh
|
||||||
|
- /bin/sh
|
||||||
|
backends:
|
||||||
|
demo: loginShell
|
||||||
|
home:
|
||||||
|
description: "Home user path"
|
||||||
|
display_name: "Home"
|
||||||
|
weight: 90
|
||||||
|
type: string
|
||||||
|
autofill:
|
||||||
|
function: lcHomeDir
|
||||||
|
args:
|
||||||
|
- $first-name
|
||||||
|
- $name
|
||||||
|
- /home/
|
||||||
|
backends:
|
||||||
|
demo: homeDirectory
|
||||||
|
password:
|
||||||
|
description: "Password of the user"
|
||||||
|
display_name: "Password"
|
||||||
|
weight: 31
|
||||||
|
self: True
|
||||||
|
type: password
|
||||||
|
backends:
|
||||||
|
demo: userPassword
|
25
goodies/demo_backend_configs/ldapcherry.ini
Normal file
25
goodies/demo_backend_configs/ldapcherry.ini
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
[backends]
|
||||||
|
#####################################
|
||||||
|
# configuration of demo backend #
|
||||||
|
#####################################
|
||||||
|
|
||||||
|
# Name of the backend
|
||||||
|
demo.module = 'ldapcherry.backend.backendDemo'
|
||||||
|
# Display name of the Backend
|
||||||
|
demo.display_name = 'Demo Backend'
|
||||||
|
# Groups of admin user
|
||||||
|
demo.admin.groups = 'SECOFF'
|
||||||
|
# Groups of basic user
|
||||||
|
demo.basic.groups = 'Test 2, Test 1'
|
||||||
|
# Password attribute name
|
||||||
|
demo.pwd_attr = 'userPassword'
|
||||||
|
# Attribute to use for the search
|
||||||
|
demo.search_attributes = 'cn, sn, givenName, uid'
|
||||||
|
# Login of default admin user
|
||||||
|
demo.admin.user = 'admin'
|
||||||
|
# Password of default admin user
|
||||||
|
demo.admin.password = 'admin'
|
||||||
|
# Login of default basic user
|
||||||
|
demo.basic.user = 'user'
|
||||||
|
# Password of default basic user
|
||||||
|
demo.basic.password = 'user'
|
36
goodies/demo_backend_configs/roles.yml
Normal file
36
goodies/demo_backend_configs/roles.yml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
sec-officer:
|
||||||
|
display_name: Security Officer
|
||||||
|
description: Security officer of the system
|
||||||
|
LC_admins: True
|
||||||
|
backends_groups:
|
||||||
|
demo:
|
||||||
|
- SECOFF
|
||||||
|
admin-lv3:
|
||||||
|
display_name: Administrators Level 3
|
||||||
|
description: Super administrators of the system
|
||||||
|
backends_groups:
|
||||||
|
demo:
|
||||||
|
- cn=dns admins,ou=Group,dc=example,dc=org
|
||||||
|
- cn=nagios admins,ou=Group,dc=example,dc=org
|
||||||
|
- cn=puppet admins,ou=Group,dc=example,dc=org
|
||||||
|
- cn=users,ou=Group,dc=example,dc=org
|
||||||
|
admin-lv2:
|
||||||
|
display_name: Administrators Level 2
|
||||||
|
description: Basic administrators of the system
|
||||||
|
backends_groups:
|
||||||
|
demo:
|
||||||
|
- cn=nagios admins,ou=Group,dc=example,dc=org
|
||||||
|
- cn=users,ou=Group,dc=example,dc=org
|
||||||
|
developpers:
|
||||||
|
display_name: Developpers
|
||||||
|
description: Developpers of the system
|
||||||
|
backends_groups:
|
||||||
|
demo:
|
||||||
|
- cn=developpers,ou=Group,dc=example,dc=org
|
||||||
|
- cn=users,ou=Group,dc=example,dc=org
|
||||||
|
users:
|
||||||
|
display_name: Simple Users
|
||||||
|
description: Basic users of the system
|
||||||
|
backends_groups:
|
||||||
|
demo:
|
||||||
|
- cn=users,ou=Group,dc=example,dc=org
|
18
goodies/gen-dev-conf.sh
Executable file
18
goodies/gen-dev-conf.sh
Executable file
@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
ROOT=$(readlink -f $(dirname $0)/../)
|
||||||
|
|
||||||
|
cp $ROOT/conf/ldapcherry.ini $ROOT/ldapcherry-dev.ini
|
||||||
|
|
||||||
|
sed -i "s|/etc/ldapcherry/|$ROOT/conf/|" $ROOT/ldapcherry-dev.ini
|
||||||
|
sed -i "s|/usr/share/ldapcherry/|$ROOT/resources/|" $ROOT/ldapcherry-dev.ini
|
||||||
|
sed -i "s|^ldap\.|#ldap.|" $ROOT/ldapcherry-dev.ini
|
||||||
|
sed -i "s|#demo\.|ldap.|" $ROOT/ldapcherry-dev.ini
|
||||||
|
|
||||||
|
GROUPS='cn=nagios admins\\,ou=Group\\,dc=example\\,dc=org, cn=users\\,ou=Group\\,dc=example\\,dc=org'
|
||||||
|
sed -i "s|ldap.admin.groups.*|ldap.admin.groups = '$GROUPS'|" $ROOT/ldapcherry-dev.ini
|
||||||
|
|
||||||
|
|
||||||
|
sed -i "s|^min_length.*|min_length = 3|" $ROOT/ldapcherry-dev.ini
|
||||||
|
sed -i "s|^min_upper.*|min_upper = 0|" $ROOT/ldapcherry-dev.ini
|
||||||
|
sed -i "s|^min_digit.*|min_digit = 0|" $ROOT/ldapcherry-dev.ini
|
25
goodies/nginx-fastcgi.conf
Normal file
25
goodies/nginx-fastcgi.conf
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
server {
|
||||||
|
listen 80 default_server;
|
||||||
|
listen [::]:80 default_server;
|
||||||
|
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
fastcgi_param REQUEST_METHOD $request_method;
|
||||||
|
fastcgi_param QUERY_STRING $query_string;
|
||||||
|
fastcgi_param CONTENT_TYPE $content_type;
|
||||||
|
fastcgi_param CONTENT_LENGTH $content_length;
|
||||||
|
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
|
||||||
|
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
|
||||||
|
fastcgi_param REMOTE_ADDR $remote_addr;
|
||||||
|
fastcgi_param REMOTE_PORT $remote_port;
|
||||||
|
fastcgi_param SERVER_ADDR $server_addr;
|
||||||
|
fastcgi_param SERVER_PORT $server_port;
|
||||||
|
fastcgi_param SERVER_NAME $server_name;
|
||||||
|
fastcgi_param SERVER_PROTOCOL $server_protocol;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_script_name;
|
||||||
|
|
||||||
|
fastcgi_pass 127.0.0.1:8080;
|
||||||
|
}
|
||||||
|
}
|
8
goodies/tag.sh
Executable file
8
goodies/tag.sh
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
cd `dirname $0`/../
|
||||||
|
|
||||||
|
version=`sed -e "s/version\ * = \ *'\(.*\)'.*/\1/;tx;d;:x" ./ldapcherry/version.py`
|
||||||
|
|
||||||
|
git tag "$version" -m "version $version"
|
||||||
|
git push origin "$version"
|
@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# vim:set expandtab tabstop=4 shiftwidth=4:
|
# vim:set expandtab tabstop=4 shiftwidth=4:
|
||||||
#
|
#
|
||||||
@ -8,6 +7,7 @@
|
|||||||
|
|
||||||
# Generic imports
|
# Generic imports
|
||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import traceback
|
import traceback
|
||||||
import json
|
import json
|
||||||
@ -15,10 +15,8 @@ import logging
|
|||||||
import logging.handlers
|
import logging.handlers
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from socket import error as socket_error
|
from socket import error as socket_error
|
||||||
import base64
|
|
||||||
import cgi
|
|
||||||
|
|
||||||
from exceptions import *
|
from ldapcherry.exceptions import *
|
||||||
from ldapcherry.lclogging import *
|
from ldapcherry.lclogging import *
|
||||||
from ldapcherry.roles import Roles
|
from ldapcherry.roles import Roles
|
||||||
from ldapcherry.attributes import Attributes
|
from ldapcherry.attributes import Attributes
|
||||||
@ -30,7 +28,14 @@ from cherrypy.lib.httputil import parse_query_string
|
|||||||
# Mako template engines imports
|
# Mako template engines imports
|
||||||
from mako.template import Template
|
from mako.template import Template
|
||||||
from mako import lookup
|
from mako import lookup
|
||||||
from sets import Set
|
from mako import exceptions
|
||||||
|
|
||||||
|
|
||||||
|
if sys.version < '3':
|
||||||
|
from sets import Set as set
|
||||||
|
from urllib import quote_plus
|
||||||
|
else:
|
||||||
|
from urllib.parse import quote_plus
|
||||||
|
|
||||||
SESSION_KEY = '_cp_username'
|
SESSION_KEY = '_cp_username'
|
||||||
|
|
||||||
@ -55,36 +60,6 @@ class LdapCherry(object):
|
|||||||
traceback=True
|
traceback=True
|
||||||
)
|
)
|
||||||
|
|
||||||
def _escape_list(self, data):
|
|
||||||
ret = []
|
|
||||||
for i in data:
|
|
||||||
ret.append(cgi.escape(i, True))
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def _escape_dict(self, data):
|
|
||||||
for d in data:
|
|
||||||
if isinstance(data[d], list):
|
|
||||||
data[d] = self._escape_list(data[d])
|
|
||||||
elif isinstance(data[d], dict):
|
|
||||||
data[d] = self._escape_dict(data[d])
|
|
||||||
elif isinstance(data[d], Set):
|
|
||||||
data[d] = Set(self._escape_list(data[d]))
|
|
||||||
else:
|
|
||||||
data[d] = cgi.escape(data[d], True)
|
|
||||||
return data
|
|
||||||
|
|
||||||
def _escape(self, data, dtype):
|
|
||||||
if data is None:
|
|
||||||
return None
|
|
||||||
elif dtype == 'search_list':
|
|
||||||
for d in data:
|
|
||||||
data[d] = self._escape_dict(data[d])
|
|
||||||
elif dtype == 'attr_list':
|
|
||||||
data = self._escape_dict(data)
|
|
||||||
elif dtype == 'lonely_groups':
|
|
||||||
data = self._escape_dict(data)
|
|
||||||
return data
|
|
||||||
|
|
||||||
def _get_param(self, section, key, config, default=None):
|
def _get_param(self, section, key, config, default=None):
|
||||||
""" Get configuration parameter "key" from config
|
""" Get configuration parameter "key" from config
|
||||||
@str section: the section of the config file
|
@str section: the section of the config file
|
||||||
@ -143,10 +118,10 @@ class LdapCherry(object):
|
|||||||
backends = self.backends_params.keys()
|
backends = self.backends_params.keys()
|
||||||
for b in self.roles.get_backends():
|
for b in self.roles.get_backends():
|
||||||
if b not in backends:
|
if b not in backends:
|
||||||
raise MissingBackend(b)
|
raise MissingBackend(b, 'role')
|
||||||
for b in self.roles.get_backends():
|
for b in self.attributes.get_backends():
|
||||||
if b not in backends:
|
if b not in backends:
|
||||||
raise MissingBackend(b)
|
raise MissingBackend(b, 'attribute')
|
||||||
|
|
||||||
def _init_backends(self, config):
|
def _init_backends(self, config):
|
||||||
""" Init all backends
|
""" Init all backends
|
||||||
@ -167,7 +142,7 @@ class LdapCherry(object):
|
|||||||
try:
|
try:
|
||||||
self.backends_display_names[backend] = \
|
self.backends_display_names[backend] = \
|
||||||
self.backends_params[backend]['display_name']
|
self.backends_params[backend]['display_name']
|
||||||
except:
|
except Exception as e:
|
||||||
self.backends_display_names[backend] = backend
|
self.backends_display_names[backend] = backend
|
||||||
self.backends_params[backend]['display_name'] = backend
|
self.backends_params[backend]['display_name'] = backend
|
||||||
params = self.backends_params[backend]
|
params = self.backends_params[backend]
|
||||||
@ -177,7 +152,7 @@ class LdapCherry(object):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise MissingParameter('backends', backend + '.module')
|
raise MissingParameter('backends', backend + '.module')
|
||||||
try:
|
try:
|
||||||
bc = __import__(module, globals(), locals(), ['Backend'], -1)
|
bc = __import__(module, globals(), locals(), ['Backend'], 0)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._handle_exception(e)
|
self._handle_exception(e)
|
||||||
raise BackendModuleLoadingFail(module)
|
raise BackendModuleLoadingFail(module)
|
||||||
@ -186,7 +161,7 @@ class LdapCherry(object):
|
|||||||
key = self.attributes.get_backend_key(backend)
|
key = self.attributes.get_backend_key(backend)
|
||||||
self.backends[backend] = bc.Backend(
|
self.backends[backend] = bc.Backend(
|
||||||
params,
|
params,
|
||||||
cherrypy.log,
|
cherrypy.log.error,
|
||||||
backend,
|
backend,
|
||||||
attrslist,
|
attrslist,
|
||||||
key,
|
key,
|
||||||
@ -218,8 +193,8 @@ class LdapCherry(object):
|
|||||||
'ldapcherry.ppolicy'
|
'ldapcherry.ppolicy'
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
pp = __import__(module, globals(), locals(), ['PPolicy'], -1)
|
pp = __import__(module, globals(), locals(), ['PPolicy'], 0)
|
||||||
except:
|
except Exception as e:
|
||||||
raise BackendModuleLoadingFail(module)
|
raise BackendModuleLoadingFail(module)
|
||||||
if 'ppolicy' in config:
|
if 'ppolicy' in config:
|
||||||
ppcfg = config['ppolicy']
|
ppcfg = config['ppolicy']
|
||||||
@ -237,7 +212,7 @@ class LdapCherry(object):
|
|||||||
elif self.auth_mode == 'custom':
|
elif self.auth_mode == 'custom':
|
||||||
# load custom auth module
|
# load custom auth module
|
||||||
auth_module = self._get_param('auth', 'auth.module', config)
|
auth_module = self._get_param('auth', 'auth.module', config)
|
||||||
auth = __import__(auth_module, globals(), locals(), ['Auth'], -1)
|
auth = __import__(auth_module, globals(), locals(), ['Auth'], 0)
|
||||||
self.auth = auth.Auth(config['auth'], cherrypy.log)
|
self.auth = auth.Auth(config['auth'], cherrypy.log)
|
||||||
else:
|
else:
|
||||||
raise WrongParamValue(
|
raise WrongParamValue(
|
||||||
@ -278,6 +253,15 @@ class LdapCherry(object):
|
|||||||
handler.setFormatter(syslog_formatter)
|
handler.setFormatter(syslog_formatter)
|
||||||
cherrypy.log.access_log.addHandler(handler)
|
cherrypy.log.access_log.addHandler(handler)
|
||||||
|
|
||||||
|
# if stdout, open a logger on stdout
|
||||||
|
elif access_handler == 'stdout':
|
||||||
|
cherrypy.log.access_log.handlers = []
|
||||||
|
handler = logging.StreamHandler(sys.stdout)
|
||||||
|
formatter = logging.Formatter(
|
||||||
|
'ldapcherry.access - %(levelname)s - %(message)s'
|
||||||
|
)
|
||||||
|
handler.setFormatter(formatter)
|
||||||
|
cherrypy.log.access_log.addHandler(handler)
|
||||||
# if file, we keep the default
|
# if file, we keep the default
|
||||||
elif access_handler == 'file':
|
elif access_handler == 'file':
|
||||||
pass
|
pass
|
||||||
@ -323,6 +307,15 @@ class LdapCherry(object):
|
|||||||
handler.setFormatter(syslog_formatter)
|
handler.setFormatter(syslog_formatter)
|
||||||
cherrypy.log.error_log.addHandler(handler)
|
cherrypy.log.error_log.addHandler(handler)
|
||||||
|
|
||||||
|
# if stdout, open a logger on stdout
|
||||||
|
elif error_handler == 'stdout':
|
||||||
|
cherrypy.log.error_log.handlers = []
|
||||||
|
handler = logging.StreamHandler(sys.stdout)
|
||||||
|
formatter = logging.Formatter(
|
||||||
|
'ldapcherry.app - %(levelname)s - %(message)s'
|
||||||
|
)
|
||||||
|
handler.setFormatter(formatter)
|
||||||
|
cherrypy.log.error_log.addHandler(handler)
|
||||||
# if file, we keep the default
|
# if file, we keep the default
|
||||||
elif error_handler == 'file':
|
elif error_handler == 'file':
|
||||||
pass
|
pass
|
||||||
@ -337,6 +330,7 @@ class LdapCherry(object):
|
|||||||
cherrypy.log.error_log.setLevel(level)
|
cherrypy.log.error_log.setLevel(level)
|
||||||
|
|
||||||
if debug:
|
if debug:
|
||||||
|
cherrypy.log.error_log.handlers = []
|
||||||
handler = logging.StreamHandler(sys.stderr)
|
handler = logging.StreamHandler(sys.stderr)
|
||||||
handler.setLevel(logging.DEBUG)
|
handler.setLevel(logging.DEBUG)
|
||||||
cherrypy.log.error_log.addHandler(handler)
|
cherrypy.log.error_log.addHandler(handler)
|
||||||
@ -386,7 +380,8 @@ class LdapCherry(object):
|
|||||||
)
|
)
|
||||||
# preload templates
|
# preload templates
|
||||||
self.temp_lookup = lookup.TemplateLookup(
|
self.temp_lookup = lookup.TemplateLookup(
|
||||||
directories=self.template_dir, input_encoding='utf-8'
|
directories=self.template_dir, input_encoding='utf-8',
|
||||||
|
default_filters=['unicode', 'h']
|
||||||
)
|
)
|
||||||
# load each template
|
# load each template
|
||||||
self.temp = {}
|
self.temp = {}
|
||||||
@ -570,9 +565,9 @@ class LdapCherry(object):
|
|||||||
return 'anonymous'
|
return 'anonymous'
|
||||||
return cherrypy.session.get(SESSION_KEY)
|
return cherrypy.session.get(SESSION_KEY)
|
||||||
|
|
||||||
def _check_auth(self, must_admin):
|
def _check_auth(self, must_admin, redir_login=True):
|
||||||
""" check if a user is autheticated and, optionnaly an administrator
|
""" check if a user is autheticated and, optionnaly an administrator
|
||||||
if user not authentifaced -> redirection to login page (with base64
|
if user not authenticated -> redirect to login page (with escaped URL
|
||||||
of the originaly requested page (redirection after login)
|
of the originaly requested page (redirection after login)
|
||||||
if user authenticated, not admin and must_admin enabled -> 403 error
|
if user authenticated, not admin and must_admin enabled -> 403 error
|
||||||
@boolean must_admin: flag "user must be an administrator to access
|
@boolean must_admin: flag "user must be an administrator to access
|
||||||
@ -587,19 +582,32 @@ class LdapCherry(object):
|
|||||||
qs = ''
|
qs = ''
|
||||||
else:
|
else:
|
||||||
qs = '?' + cherrypy.request.query_string
|
qs = '?' + cherrypy.request.query_string
|
||||||
# base64 of the requested URL
|
# Escaped version of the requested URL
|
||||||
b64requrl = base64.b64encode(cherrypy.url() + qs)
|
quoted_requrl = quote_plus(cherrypy.url() + qs)
|
||||||
if not username:
|
if not username:
|
||||||
# return to login page (with base64 of the url in query string
|
# return to login page (with quoted url in query string)
|
||||||
raise cherrypy.HTTPRedirect(
|
if redir_login:
|
||||||
"/signin?url=%(url)s" % {'url': b64requrl},
|
raise cherrypy.HTTPRedirect(
|
||||||
)
|
"/signin?url=%(url)s" % {'url': quoted_requrl},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise cherrypy.HTTPError(
|
||||||
|
"403 Forbidden",
|
||||||
|
"You must be logged in to access this ressource.",
|
||||||
|
)
|
||||||
|
|
||||||
if 'connected' not in cherrypy.session \
|
if 'connected' not in cherrypy.session \
|
||||||
or not cherrypy.session['connected']:
|
or not cherrypy.session['connected']:
|
||||||
raise cherrypy.HTTPRedirect(
|
if redir_login:
|
||||||
"/signin?url=%(url)s" % {'url': b64requrl},
|
raise cherrypy.HTTPRedirect(
|
||||||
)
|
"/signin?url=%(url)s" % {'url': quoted_requrl},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise cherrypy.HTTPError(
|
||||||
|
"403 Forbidden",
|
||||||
|
"You must be logged in to access this ressource.",
|
||||||
|
)
|
||||||
|
|
||||||
if cherrypy.session['connected'] and \
|
if cherrypy.session['connected'] and \
|
||||||
not cherrypy.session['isadmin']:
|
not cherrypy.session['isadmin']:
|
||||||
if must_admin:
|
if must_admin:
|
||||||
@ -610,13 +618,20 @@ class LdapCherry(object):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return username
|
return username
|
||||||
|
|
||||||
if cherrypy.session['connected'] and \
|
if cherrypy.session['connected'] and \
|
||||||
cherrypy.session['isadmin']:
|
cherrypy.session['isadmin']:
|
||||||
return username
|
return username
|
||||||
else:
|
else:
|
||||||
raise cherrypy.HTTPRedirect(
|
if redir_login:
|
||||||
"/signin?url=%(url)s" % {'url': b64requrl},
|
raise cherrypy.HTTPRedirect(
|
||||||
)
|
"/signin?url=%(url)s" % {'url': quoted_requrl},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise cherrypy.HTTPError(
|
||||||
|
"403 Forbidden",
|
||||||
|
"You must be logged in to access this ressource.",
|
||||||
|
)
|
||||||
|
|
||||||
def _adduser(self, params):
|
def _adduser(self, params):
|
||||||
cherrypy.log.error(
|
cherrypy.log.error(
|
||||||
@ -641,13 +656,23 @@ class LdapCherry(object):
|
|||||||
if b not in badd:
|
if b not in badd:
|
||||||
badd[b] = {}
|
badd[b] = {}
|
||||||
badd[b][backends[b]] = params['attrs'][attr]
|
badd[b][backends[b]] = params['attrs'][attr]
|
||||||
|
added = False
|
||||||
for b in badd:
|
for b in badd:
|
||||||
self.backends[b].add_user(badd[b])
|
try:
|
||||||
|
self.backends[b].add_user(badd[b])
|
||||||
|
added = True
|
||||||
|
except UserAlreadyExists as e:
|
||||||
|
self._add_notification(
|
||||||
|
'User already exists in backend "' + b + '"'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if not added:
|
||||||
|
raise e
|
||||||
|
|
||||||
key = self.attributes.get_key()
|
key = self.attributes.get_key()
|
||||||
username = params['attrs'][key]
|
username = params['attrs'][key]
|
||||||
sess = cherrypy.session
|
sess = cherrypy.session
|
||||||
admin = sess.get(SESSION_KEY, None)
|
admin = sess.get(SESSION_KEY, 'unknown')
|
||||||
|
|
||||||
cherrypy.log.error(
|
cherrypy.log.error(
|
||||||
msg="user '" + username + "' added by '" + admin + "'",
|
msg="user '" + username + "' added by '" + admin + "'",
|
||||||
@ -664,7 +689,7 @@ class LdapCherry(object):
|
|||||||
roles.append(r)
|
roles.append(r)
|
||||||
groups = self.roles.get_groups(roles)
|
groups = self.roles.get_groups(roles)
|
||||||
for b in groups:
|
for b in groups:
|
||||||
self.backends[b].add_to_groups(username, Set(groups[b]))
|
self.backends[b].add_to_groups(username, set(groups[b]))
|
||||||
|
|
||||||
cherrypy.log.error(
|
cherrypy.log.error(
|
||||||
msg="user '" + username + "' made member of " +
|
msg="user '" + username + "' made member of " +
|
||||||
@ -699,7 +724,13 @@ class LdapCherry(object):
|
|||||||
badd[b] = {}
|
badd[b] = {}
|
||||||
badd[b][backends[b]] = params['attrs'][attr]
|
badd[b][backends[b]] = params['attrs'][attr]
|
||||||
for b in badd:
|
for b in badd:
|
||||||
self.backends[b].set_attrs(username, badd[b])
|
try:
|
||||||
|
self.backends[b].set_attrs(username, badd[b])
|
||||||
|
except UserDoesntExist as e:
|
||||||
|
self._add_notification(
|
||||||
|
'User does not exist in backend "' + b + '"'
|
||||||
|
)
|
||||||
|
|
||||||
return badd
|
return badd
|
||||||
|
|
||||||
def _selfmodify(self, params):
|
def _selfmodify(self, params):
|
||||||
@ -738,7 +769,7 @@ class LdapCherry(object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
sess = cherrypy.session
|
sess = cherrypy.session
|
||||||
admin = sess.get(SESSION_KEY, None)
|
admin = sess.get(SESSION_KEY, 'unknown')
|
||||||
|
|
||||||
cherrypy.log.error(
|
cherrypy.log.error(
|
||||||
msg="user '" + username + "' modified by '" + admin + "'",
|
msg="user '" + username + "' modified by '" + admin + "'",
|
||||||
@ -786,10 +817,10 @@ class LdapCherry(object):
|
|||||||
if b not in g:
|
if b not in g:
|
||||||
g[b] = []
|
g[b] = []
|
||||||
tmp = \
|
tmp = \
|
||||||
Set(groups_add[b]) - \
|
set(groups_add[b]) - \
|
||||||
Set(groups_keep[b]) - \
|
set(groups_keep[b]) - \
|
||||||
Set(groups_current[b]) - \
|
set(groups_current[b]) - \
|
||||||
Set(lonely_groups[b])
|
set(lonely_groups[b])
|
||||||
cherrypy.log.error(
|
cherrypy.log.error(
|
||||||
msg="user '" + username + "' added to groups: " +
|
msg="user '" + username + "' added to groups: " +
|
||||||
str(list(tmp)) + " in backend '" + b + "'",
|
str(list(tmp)) + " in backend '" + b + "'",
|
||||||
@ -803,11 +834,11 @@ class LdapCherry(object):
|
|||||||
g[b] = []
|
g[b] = []
|
||||||
tmp = \
|
tmp = \
|
||||||
(
|
(
|
||||||
(Set(groups_rm[b]) | Set(groups_remove[b])) -
|
(set(groups_rm[b]) | set(groups_remove[b])) -
|
||||||
(Set(groups_keep[b]) | Set(groups_add[b]))
|
(set(groups_keep[b]) | set(groups_add[b]))
|
||||||
) & \
|
) & \
|
||||||
(
|
(
|
||||||
Set(groups_current[b]) | Set(lonely_groups[b])
|
set(groups_current[b]) | set(lonely_groups[b])
|
||||||
)
|
)
|
||||||
cherrypy.log.error(
|
cherrypy.log.error(
|
||||||
msg="user '" + username + "' removed from groups: " +
|
msg="user '" + username + "' removed from groups: " +
|
||||||
@ -824,10 +855,17 @@ class LdapCherry(object):
|
|||||||
|
|
||||||
def _deleteuser(self, username):
|
def _deleteuser(self, username):
|
||||||
sess = cherrypy.session
|
sess = cherrypy.session
|
||||||
admin = sess.get(SESSION_KEY, None)
|
admin = sess.get(SESSION_KEY, 'unknown')
|
||||||
|
|
||||||
for b in self.backends:
|
for b in self.backends:
|
||||||
self.backends[b].del_user(username)
|
try:
|
||||||
|
self.backends[b].del_user(username)
|
||||||
|
except UserDoesntExist as e:
|
||||||
|
cherrypy.log.error(
|
||||||
|
msg="User '" + username +
|
||||||
|
"' didn't exist in backend '" + b + "'",
|
||||||
|
severity=logging.INFO
|
||||||
|
)
|
||||||
cherrypy.log.error(
|
cherrypy.log.error(
|
||||||
msg="user '" + username + "' deleted from backend '" + b + "'",
|
msg="user '" + username + "' deleted from backend '" + b + "'",
|
||||||
severity=logging.DEBUG
|
severity=logging.DEBUG
|
||||||
@ -876,7 +914,7 @@ class LdapCherry(object):
|
|||||||
if url is None:
|
if url is None:
|
||||||
redirect = "/"
|
redirect = "/"
|
||||||
else:
|
else:
|
||||||
redirect = base64.b64decode(url)
|
redirect = url
|
||||||
raise cherrypy.HTTPRedirect(redirect)
|
raise cherrypy.HTTPRedirect(redirect)
|
||||||
else:
|
else:
|
||||||
message = "login failed for user '%(user)s'" % {
|
message = "login failed for user '%(user)s'" % {
|
||||||
@ -889,7 +927,7 @@ class LdapCherry(object):
|
|||||||
if url is None:
|
if url is None:
|
||||||
qs = ''
|
qs = ''
|
||||||
else:
|
else:
|
||||||
qs = '?url=' + url
|
qs = '?url=' + quote_plus(url)
|
||||||
raise cherrypy.HTTPRedirect("/signin" + qs)
|
raise cherrypy.HTTPRedirect("/signin" + qs)
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
@ -926,7 +964,7 @@ class LdapCherry(object):
|
|||||||
return self.temp['index.tmpl'].render(
|
return self.temp['index.tmpl'].render(
|
||||||
is_admin=is_admin,
|
is_admin=is_admin,
|
||||||
attrs_list=attrs_list,
|
attrs_list=attrs_list,
|
||||||
searchresult=self._escape(user_attrs, 'attr_list'),
|
searchresult=user_attrs,
|
||||||
notifications=self._empty_notification(),
|
notifications=self._empty_notification(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -942,7 +980,7 @@ class LdapCherry(object):
|
|||||||
res = None
|
res = None
|
||||||
attrs_list = self.attributes.get_search_attributes()
|
attrs_list = self.attributes.get_search_attributes()
|
||||||
return self.temp['searchuser.tmpl'].render(
|
return self.temp['searchuser.tmpl'].render(
|
||||||
searchresult=self._escape(res, 'search_list'),
|
searchresult=res,
|
||||||
attrs_list=attrs_list,
|
attrs_list=attrs_list,
|
||||||
is_admin=is_admin,
|
is_admin=is_admin,
|
||||||
custom_js=self.custom_js,
|
custom_js=self.custom_js,
|
||||||
@ -953,8 +991,8 @@ class LdapCherry(object):
|
|||||||
@exception_decorator
|
@exception_decorator
|
||||||
def checkppolicy(self, **params):
|
def checkppolicy(self, **params):
|
||||||
""" search user page """
|
""" search user page """
|
||||||
self._check_auth(must_admin=False)
|
self._check_auth(must_admin=False, redir_login=False)
|
||||||
keys = params.keys()
|
keys = list(params.keys())
|
||||||
if len(keys) != 1:
|
if len(keys) != 1:
|
||||||
cherrypy.response.status = 400
|
cherrypy.response.status = 400
|
||||||
return "bad argument"
|
return "bad argument"
|
||||||
@ -979,7 +1017,7 @@ class LdapCherry(object):
|
|||||||
res = None
|
res = None
|
||||||
attrs_list = self.attributes.get_search_attributes()
|
attrs_list = self.attributes.get_search_attributes()
|
||||||
return self.temp['searchadmin.tmpl'].render(
|
return self.temp['searchadmin.tmpl'].render(
|
||||||
searchresult=self._escape(res, 'search_list'),
|
searchresult=res,
|
||||||
attrs_list=attrs_list,
|
attrs_list=attrs_list,
|
||||||
is_admin=is_admin,
|
is_admin=is_admin,
|
||||||
custom_js=self.custom_js,
|
custom_js=self.custom_js,
|
||||||
@ -1008,26 +1046,31 @@ class LdapCherry(object):
|
|||||||
for r in self.roles.flatten:
|
for r in self.roles.flatten:
|
||||||
display_names[r] = self.roles.flatten[r]['display_name']
|
display_names[r] = self.roles.flatten[r]['display_name']
|
||||||
roles_js = json.dumps(display_names, separators=(',', ':'))
|
roles_js = json.dumps(display_names, separators=(',', ':'))
|
||||||
form = self.temp['form.tmpl'].render(
|
try:
|
||||||
attributes=self.attributes.attributes,
|
form = self.temp['form.tmpl'].render(
|
||||||
values=None,
|
attributes=self.attributes.attributes,
|
||||||
modify=False,
|
values=None,
|
||||||
autofill=True
|
modify=False,
|
||||||
)
|
autofill=True
|
||||||
roles = self.temp['roles.tmpl'].render(
|
)
|
||||||
roles=self.roles.flatten,
|
roles = self.temp['roles.tmpl'].render(
|
||||||
graph=self.roles.graph,
|
roles=self.roles.flatten,
|
||||||
graph_js=graph_js,
|
graph=self.roles.graph,
|
||||||
roles_js=roles_js,
|
graph_js=graph_js,
|
||||||
current_roles=None,
|
roles_js=roles_js,
|
||||||
)
|
current_roles=None,
|
||||||
return self.temp['adduser.tmpl'].render(
|
)
|
||||||
form=form,
|
return self.temp['adduser.tmpl'].render(
|
||||||
roles=roles,
|
form=form,
|
||||||
is_admin=is_admin,
|
roles=roles,
|
||||||
custom_js=self.custom_js,
|
is_admin=is_admin,
|
||||||
notifications=self._empty_notification(),
|
custom_js=self.custom_js,
|
||||||
)
|
notifications=self._empty_notification(),
|
||||||
|
)
|
||||||
|
except NameError:
|
||||||
|
raise TemplateRenderError(
|
||||||
|
exceptions.text_error_template().render()
|
||||||
|
)
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
@exception_decorator
|
@exception_decorator
|
||||||
@ -1037,7 +1080,7 @@ class LdapCherry(object):
|
|||||||
is_admin = self._check_admin()
|
is_admin = self._check_admin()
|
||||||
try:
|
try:
|
||||||
referer = cherrypy.request.headers['Referer']
|
referer = cherrypy.request.headers['Referer']
|
||||||
except:
|
except Exception as e:
|
||||||
referer = '/'
|
referer = '/'
|
||||||
self._deleteuser(user)
|
self._deleteuser(user)
|
||||||
self._add_notification('User Deleted')
|
self._add_notification('User Deleted')
|
||||||
@ -1056,7 +1099,7 @@ class LdapCherry(object):
|
|||||||
self._add_notification("User modified")
|
self._add_notification("User modified")
|
||||||
try:
|
try:
|
||||||
referer = cherrypy.request.headers['Referer']
|
referer = cherrypy.request.headers['Referer']
|
||||||
except:
|
except Exception as e:
|
||||||
referer = '/'
|
referer = '/'
|
||||||
raise cherrypy.HTTPRedirect(referer)
|
raise cherrypy.HTTPRedirect(referer)
|
||||||
|
|
||||||
@ -1069,6 +1112,15 @@ class LdapCherry(object):
|
|||||||
display_names = {}
|
display_names = {}
|
||||||
for r in self.roles.flatten:
|
for r in self.roles.flatten:
|
||||||
display_names[r] = self.roles.flatten[r]['display_name']
|
display_names[r] = self.roles.flatten[r]['display_name']
|
||||||
|
|
||||||
|
if user is None:
|
||||||
|
cherrypy.response.status = 400
|
||||||
|
return self.temp['error.tmpl'].render(
|
||||||
|
is_admin=is_admin,
|
||||||
|
alert='warning',
|
||||||
|
message="No user requested"
|
||||||
|
)
|
||||||
|
|
||||||
user_attrs = self._get_user(user)
|
user_attrs = self._get_user(user)
|
||||||
if user_attrs == {}:
|
if user_attrs == {}:
|
||||||
cherrypy.response.status = 400
|
cherrypy.response.status = 400
|
||||||
@ -1082,33 +1134,43 @@ class LdapCherry(object):
|
|||||||
standalone_groups = tmp['unusedgroups']
|
standalone_groups = tmp['unusedgroups']
|
||||||
roles_js = json.dumps(display_names, separators=(',', ':'))
|
roles_js = json.dumps(display_names, separators=(',', ':'))
|
||||||
key = self.attributes.get_key()
|
key = self.attributes.get_key()
|
||||||
form = self.temp['form.tmpl'].render(
|
|
||||||
attributes=self.attributes.attributes,
|
try:
|
||||||
values=self._escape(user_attrs, 'attr_list'),
|
form = self.temp['form.tmpl'].render(
|
||||||
modify=True,
|
attributes=self.attributes.attributes,
|
||||||
keyattr=key,
|
values=user_attrs,
|
||||||
autofill=False
|
modify=True,
|
||||||
|
keyattr=key,
|
||||||
|
autofill=False
|
||||||
|
)
|
||||||
|
|
||||||
|
roles = self.temp['roles.tmpl'].render(
|
||||||
|
roles=self.roles.flatten,
|
||||||
|
graph=self.roles.graph,
|
||||||
|
graph_js=graph_js,
|
||||||
|
roles_js=roles_js,
|
||||||
|
current_roles=user_roles,
|
||||||
)
|
)
|
||||||
roles = self.temp['roles.tmpl'].render(
|
|
||||||
roles=self.roles.flatten,
|
glued_template = self.temp['modify.tmpl'].render(
|
||||||
graph=self.roles.graph,
|
form=form,
|
||||||
graph_js=graph_js,
|
roles=roles,
|
||||||
roles_js=roles_js,
|
is_admin=is_admin,
|
||||||
current_roles=user_roles,
|
standalone_groups=standalone_groups,
|
||||||
)
|
backends_display_names=self.backends_display_names,
|
||||||
return self.temp['modify.tmpl'].render(
|
custom_js=self.custom_js,
|
||||||
form=form,
|
notifications=self._empty_notification(),
|
||||||
roles=roles,
|
|
||||||
is_admin=is_admin,
|
|
||||||
standalone_groups=self._escape(standalone_groups, 'lonely_groups'),
|
|
||||||
backends_display_names=self.backends_display_names,
|
|
||||||
custom_js=self.custom_js,
|
|
||||||
notifications=self._empty_notification(),
|
|
||||||
)
|
)
|
||||||
|
except NameError:
|
||||||
|
raise TemplateRenderError(
|
||||||
|
exceptions.text_error_template().render()
|
||||||
|
)
|
||||||
|
|
||||||
|
return glued_template
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
@exception_decorator
|
@exception_decorator
|
||||||
def default(self, attr=''):
|
def default(self, attr='', *args, **params):
|
||||||
cherrypy.response.status = 404
|
cherrypy.response.status = 404
|
||||||
self._check_auth(must_admin=False)
|
self._check_auth(must_admin=False)
|
||||||
is_admin = self._check_admin()
|
is_admin = self._check_admin()
|
||||||
@ -1138,20 +1200,27 @@ class LdapCherry(object):
|
|||||||
"Self modification done"
|
"Self modification done"
|
||||||
)
|
)
|
||||||
user_attrs = self._get_user(user)
|
user_attrs = self._get_user(user)
|
||||||
if user_attrs == {}:
|
|
||||||
return self.temp['error.tmpl'].render(
|
try:
|
||||||
is_admin=is_admin,
|
if user_attrs == {}:
|
||||||
alert='warning',
|
return self.temp['error.tmpl'].render(
|
||||||
message="User doesn't exist"
|
is_admin=is_admin,
|
||||||
|
alert='warning',
|
||||||
|
message="User doesn't exist"
|
||||||
|
)
|
||||||
|
|
||||||
|
form = self.temp['form.tmpl'].render(
|
||||||
|
attributes=self.attributes.get_selfattributes(),
|
||||||
|
values=user_attrs,
|
||||||
|
modify=True,
|
||||||
|
autofill=False
|
||||||
)
|
)
|
||||||
form = self.temp['form.tmpl'].render(
|
return self.temp['selfmodify.tmpl'].render(
|
||||||
attributes=self.attributes.get_selfattributes(),
|
form=form,
|
||||||
values=self._escape(user_attrs, 'attr_list'),
|
is_admin=is_admin,
|
||||||
modify=True,
|
notifications=self._empty_notification(),
|
||||||
autofill=False
|
)
|
||||||
)
|
except NameError:
|
||||||
return self.temp['selfmodify.tmpl'].render(
|
raise TemplateRenderError(
|
||||||
form=form,
|
exceptions.text_error_template().render()
|
||||||
is_admin=is_admin,
|
)
|
||||||
notifications=self._empty_notification(),
|
|
||||||
)
|
|
||||||
|
@ -12,25 +12,28 @@ import re
|
|||||||
from ldapcherry.pyyamlwrapper import loadNoDump
|
from ldapcherry.pyyamlwrapper import loadNoDump
|
||||||
from ldapcherry.pyyamlwrapper import DumplicatedKey
|
from ldapcherry.pyyamlwrapper import DumplicatedKey
|
||||||
from ldapcherry.exceptions import *
|
from ldapcherry.exceptions import *
|
||||||
from sets import Set
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
if sys.version < '3':
|
||||||
|
from sets import Set as set
|
||||||
|
|
||||||
# List of available types for form
|
# List of available types for form
|
||||||
types = ['string', 'email', 'int', 'stringlist', 'fix', 'password']
|
types = ['string', 'textfield', 'email', 'int', 'stringlist',
|
||||||
|
'fix', 'password']
|
||||||
|
|
||||||
|
|
||||||
class Attributes:
|
class Attributes:
|
||||||
|
|
||||||
def __init__(self, attributes_file):
|
def __init__(self, attributes_file):
|
||||||
self.attributes_file = attributes_file
|
self.attributes_file = attributes_file
|
||||||
self.backends = Set([])
|
self.backends = set([])
|
||||||
self.self_attributes = {}
|
self.self_attributes = {}
|
||||||
self.backend_attributes = {}
|
self.backend_attributes = {}
|
||||||
self.displayed_attributes = {}
|
self.displayed_attributes = {}
|
||||||
self.key = None
|
self.key = None
|
||||||
try:
|
try:
|
||||||
stream = open(attributes_file, 'r')
|
stream = open(attributes_file, 'r')
|
||||||
except:
|
except Exception as e:
|
||||||
raise MissingAttributesFile(attributes_file)
|
raise MissingAttributesFile(attributes_file)
|
||||||
try:
|
try:
|
||||||
self.attributes = loadNoDump(stream)
|
self.attributes = loadNoDump(stream)
|
||||||
@ -68,7 +71,7 @@ class Attributes:
|
|||||||
raise MissingUserKey()
|
raise MissingUserKey()
|
||||||
|
|
||||||
def _is_email(self, email):
|
def _is_email(self, email):
|
||||||
pattern = '[\.\w]{1,}[@]\w+[.]\w+'
|
pattern = r'[\+\.\w]+@[-\.\w]+\.\w+'
|
||||||
if re.match(pattern, email):
|
if re.match(pattern, email):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
@ -81,6 +84,8 @@ class Attributes:
|
|||||||
attr_type = self.attributes[attrid]['type']
|
attr_type = self.attributes[attrid]['type']
|
||||||
if attr_type == 'string':
|
if attr_type == 'string':
|
||||||
return
|
return
|
||||||
|
elif attr_type == 'textfield':
|
||||||
|
return
|
||||||
elif attr_type == 'email':
|
elif attr_type == 'email':
|
||||||
if self._is_email(value):
|
if self._is_email(value):
|
||||||
return
|
return
|
||||||
@ -125,7 +130,9 @@ class Attributes:
|
|||||||
def get_backend_attributes(self, backend):
|
def get_backend_attributes(self, backend):
|
||||||
if backend not in self.backends:
|
if backend not in self.backends:
|
||||||
raise WrongBackend(backend)
|
raise WrongBackend(backend)
|
||||||
return self.backend_attributes[backend].keys()
|
ret = list(self.backend_attributes[backend].keys())
|
||||||
|
ret.sort()
|
||||||
|
return ret
|
||||||
|
|
||||||
def get_backend_key(self, backend):
|
def get_backend_key(self, backend):
|
||||||
if backend not in self.backends:
|
if backend not in self.backends:
|
||||||
|
@ -15,6 +15,7 @@ import ldapcherry.backend
|
|||||||
from ldapcherry.exceptions import UserDoesntExist, GroupDoesntExist
|
from ldapcherry.exceptions import UserDoesntExist, GroupDoesntExist
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
class CaFileDontExist(Exception):
|
class CaFileDontExist(Exception):
|
||||||
@ -28,6 +29,7 @@ class MissingAttr(Exception):
|
|||||||
self.log = 'attributes "cn" and "unicodePwd" must be declared ' \
|
self.log = 'attributes "cn" and "unicodePwd" must be declared ' \
|
||||||
'in attributes.yml for all Active Directory backends.'
|
'in attributes.yml for all Active Directory backends.'
|
||||||
|
|
||||||
|
|
||||||
NO_ATTR = 0
|
NO_ATTR = 0
|
||||||
DISPLAYED_ATTRS = 1
|
DISPLAYED_ATTRS = 1
|
||||||
LISTED_ATTRS = 2
|
LISTED_ATTRS = 2
|
||||||
@ -128,28 +130,38 @@ class Backend(ldapcherry.backend.backendLdap.Backend):
|
|||||||
self.dn_user_attr = 'cn'
|
self.dn_user_attr = 'cn'
|
||||||
self.key = 'sAMAccountName'
|
self.key = 'sAMAccountName'
|
||||||
self.objectclasses = [
|
self.objectclasses = [
|
||||||
'top',
|
self._byte_p23('top'),
|
||||||
'person',
|
self._byte_p23('person'),
|
||||||
'organizationalPerson',
|
self._byte_p23('organizationalPerson'),
|
||||||
'user',
|
self._byte_p23('user'),
|
||||||
'posixAccount',
|
self._byte_p23('posixAccount'),
|
||||||
]
|
]
|
||||||
self.group_attrs = {
|
self.group_attrs = {
|
||||||
'member': "%(dn)s"
|
'member': "%(dn)s"
|
||||||
}
|
}
|
||||||
|
|
||||||
self.attrlist = []
|
self.attrlist = []
|
||||||
|
self.group_attrs_keys = []
|
||||||
for a in attrslist:
|
for a in attrslist:
|
||||||
self.attrlist.append(self._str(a))
|
self.attrlist.append(self._byte_p2(a))
|
||||||
|
|
||||||
if 'cn' not in self.attrlist:
|
if self._byte_p2('cn') not in self.attrlist:
|
||||||
raise MissingAttr()
|
raise MissingAttr()
|
||||||
|
|
||||||
if 'unicodePwd' not in self.attrlist:
|
if self._byte_p2('unicodePwd') not in self.attrlist:
|
||||||
raise MissingAttr()
|
raise MissingAttr()
|
||||||
|
|
||||||
|
if sys.version < '3':
|
||||||
|
@staticmethod
|
||||||
|
def _tobyte(in_int):
|
||||||
|
return str(in_int)
|
||||||
|
else:
|
||||||
|
@staticmethod
|
||||||
|
def _tobyte(in_int):
|
||||||
|
return in_int.to_bytes(4, byteorder='big')
|
||||||
|
|
||||||
def _search_group(self, searchfilter, groupdn):
|
def _search_group(self, searchfilter, groupdn):
|
||||||
searchfilter = self._str(searchfilter)
|
searchfilter = self._byte_p2(searchfilter)
|
||||||
ldap_client = self._bind()
|
ldap_client = self._bind()
|
||||||
try:
|
try:
|
||||||
r = ldap_client.search_s(
|
r = ldap_client.search_s(
|
||||||
@ -181,22 +193,24 @@ class Backend(ldapcherry.backend.backendLdap.Backend):
|
|||||||
ldap_client = self._bind()
|
ldap_client = self._bind()
|
||||||
|
|
||||||
if by_cn:
|
if by_cn:
|
||||||
dn = self._str('CN=%(cn)s,%(user_dn)s' % {
|
dn = self._byte_p2('CN=%(cn)s,%(user_dn)s' % {
|
||||||
'cn': name,
|
'cn': name,
|
||||||
'user_dn': self.userdn
|
'user_dn': self.userdn
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
dn = name
|
dn = self._byte_p2(name)
|
||||||
|
|
||||||
attrs = {}
|
attrs = {}
|
||||||
|
|
||||||
attrs['unicodePwd'] = self._str(password_value)
|
attrs['unicodePwd'] = self._modlist(self._byte_p2(password_value))
|
||||||
|
|
||||||
ldif = modlist.modifyModlist({'unicodePwd': 'tmp'}, attrs)
|
ldif = modlist.modifyModlist({'unicodePwd': 'tmp'}, attrs)
|
||||||
ldap_client.modify_s(dn, ldif)
|
ldap_client.modify_s(dn, ldif)
|
||||||
|
|
||||||
del(attrs['unicodePwd'])
|
del(attrs['unicodePwd'])
|
||||||
attrs['UserAccountControl'] = str(NORMAL_ACCOUNT)
|
attrs['UserAccountControl'] = self._modlist(
|
||||||
|
self._tobyte(NORMAL_ACCOUNT)
|
||||||
|
)
|
||||||
ldif = modlist.modifyModlist({'UserAccountControl': 'tmp'}, attrs)
|
ldif = modlist.modifyModlist({'UserAccountControl': 'tmp'}, attrs)
|
||||||
ldap_client.modify_s(dn, ldif)
|
ldap_client.modify_s(dn, ldif)
|
||||||
|
|
||||||
@ -204,14 +218,14 @@ class Backend(ldapcherry.backend.backendLdap.Backend):
|
|||||||
password = attrs['unicodePwd']
|
password = attrs['unicodePwd']
|
||||||
del(attrs['unicodePwd'])
|
del(attrs['unicodePwd'])
|
||||||
super(Backend, self).add_user(attrs)
|
super(Backend, self).add_user(attrs)
|
||||||
userdn = self._get_user(username, NO_ATTR)
|
self._set_password(attrs['cn'], password)
|
||||||
self._set_password(userdn, password, False)
|
|
||||||
|
|
||||||
def set_attrs(self, username, attrs):
|
def set_attrs(self, username, attrs):
|
||||||
if 'unicodePwd' in attrs:
|
if 'unicodePwd' in attrs:
|
||||||
password = attrs['unicodePwd']
|
password = attrs['unicodePwd']
|
||||||
del(attrs['unicodePwd'])
|
del(attrs['unicodePwd'])
|
||||||
self._set_password(attrs['cn'], password)
|
userdn = self._get_user(self._byte_p2(username), NO_ATTR)
|
||||||
|
self._set_password(userdn, password, False)
|
||||||
super(Backend, self).set_attrs(username, attrs)
|
super(Backend, self).set_attrs(username, attrs)
|
||||||
|
|
||||||
def add_to_groups(self, username, groups):
|
def add_to_groups(self, username, groups):
|
||||||
@ -224,7 +238,7 @@ class Backend(ldapcherry.backend.backendLdap.Backend):
|
|||||||
|
|
||||||
def get_groups(self, username):
|
def get_groups(self, username):
|
||||||
username = ldap.filter.escape_filter_chars(username)
|
username = ldap.filter.escape_filter_chars(username)
|
||||||
userdn = self._get_user(username, NO_ATTR)
|
userdn = self._get_user(self._byte_p2(username), NO_ATTR)
|
||||||
|
|
||||||
searchfilter = self.group_filter_tmpl % {
|
searchfilter = self.group_filter_tmpl % {
|
||||||
'userdn': userdn,
|
'userdn': userdn,
|
||||||
@ -244,7 +258,7 @@ class Backend(ldapcherry.backend.backendLdap.Backend):
|
|||||||
)
|
)
|
||||||
|
|
||||||
for entry in groups:
|
for entry in groups:
|
||||||
ret.append(entry[1]['cn'][0])
|
ret.append(self._uni(entry[1]['cn'][0]))
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def auth(self, username, password):
|
def auth(self, username, password):
|
||||||
@ -253,7 +267,10 @@ class Backend(ldapcherry.backend.backendLdap.Backend):
|
|||||||
if binddn is not None:
|
if binddn is not None:
|
||||||
ldap_client = self._connect()
|
ldap_client = self._connect()
|
||||||
try:
|
try:
|
||||||
ldap_client.simple_bind_s(binddn, password)
|
ldap_client.simple_bind_s(
|
||||||
|
self._byte_p2(binddn),
|
||||||
|
self._byte_p2(password)
|
||||||
|
)
|
||||||
except ldap.INVALID_CREDENTIALS:
|
except ldap.INVALID_CREDENTIALS:
|
||||||
ldap_client.unbind_s()
|
ldap_client.unbind_s()
|
||||||
return False
|
return False
|
||||||
|
@ -7,12 +7,15 @@
|
|||||||
|
|
||||||
# This is a demo backend
|
# This is a demo backend
|
||||||
|
|
||||||
from sets import Set
|
|
||||||
|
import sys
|
||||||
import ldapcherry.backend
|
import ldapcherry.backend
|
||||||
from ldapcherry.exceptions import UserDoesntExist, \
|
from ldapcherry.exceptions import UserDoesntExist, \
|
||||||
GroupDoesntExist, MissingParameter, \
|
GroupDoesntExist, MissingParameter, \
|
||||||
UserAlreadyExists
|
UserAlreadyExists
|
||||||
import re
|
import re
|
||||||
|
if sys.version < '3':
|
||||||
|
from sets import Set as set
|
||||||
|
|
||||||
|
|
||||||
class Backend(ldapcherry.backend.Backend):
|
class Backend(ldapcherry.backend.Backend):
|
||||||
@ -37,13 +40,17 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
self.backend_name = name
|
self.backend_name = name
|
||||||
admin_user = self.get_param('admin.user', 'admin')
|
admin_user = self.get_param('admin.user', 'admin')
|
||||||
admin_password = self.get_param('admin.password', 'admin')
|
admin_password = self.get_param('admin.password', 'admin')
|
||||||
admin_groups = Set(re.split('\W+', self.get_param('admin.groups')))
|
admin_groups = set(
|
||||||
|
self._basic_splitter(self.get_param('admin.groups'))
|
||||||
|
)
|
||||||
basic_user = self.get_param('basic.user', 'user')
|
basic_user = self.get_param('basic.user', 'user')
|
||||||
basic_password = self.get_param('basic.password', 'user')
|
basic_password = self.get_param('basic.password', 'user')
|
||||||
basic_groups = Set(re.split('\W+', self.get_param('basic.groups')))
|
basic_groups = set(
|
||||||
|
self._basic_splitter(self.get_param('basic.groups'))
|
||||||
|
)
|
||||||
pwd_attr = self.get_param('pwd_attr')
|
pwd_attr = self.get_param('pwd_attr')
|
||||||
self.search_attrs = Set(
|
self.search_attrs = set(
|
||||||
re.split('\W+', self.get_param('search_attributes')),
|
re.split(r'\W+', self.get_param('search_attributes')),
|
||||||
)
|
)
|
||||||
self.pwd_attr = pwd_attr
|
self.pwd_attr = pwd_attr
|
||||||
self.admin_user = admin_user
|
self.admin_user = admin_user
|
||||||
@ -60,6 +67,11 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
'groups': basic_groups,
|
'groups': basic_groups,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _basic_splitter(in_str):
|
||||||
|
return [re.sub(r'(?<!\\)\\', '', x)
|
||||||
|
for x in re.split(r'(?<!\\),\W*', in_str)]
|
||||||
|
|
||||||
def _check_fix_users(self, username):
|
def _check_fix_users(self, username):
|
||||||
if self.admin_user == username or self.basic_user == username:
|
if self.admin_user == username or self.basic_user == username:
|
||||||
raise Exception('User cannot be modified')
|
raise Exception('User cannot be modified')
|
||||||
@ -91,7 +103,7 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
if username in self.users:
|
if username in self.users:
|
||||||
raise UserAlreadyExists(username, self.backend_name)
|
raise UserAlreadyExists(username, self.backend_name)
|
||||||
self.users[username] = attrs
|
self.users[username] = attrs
|
||||||
self.users[username]['groups'] = Set([])
|
self.users[username]['groups'] = set([])
|
||||||
|
|
||||||
def del_user(self, username):
|
def del_user(self, username):
|
||||||
""" Delete a user from the backend
|
""" Delete a user from the backend
|
||||||
@ -103,11 +115,11 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
self._check_fix_users(username)
|
self._check_fix_users(username)
|
||||||
try:
|
try:
|
||||||
del self.users[username]
|
del self.users[username]
|
||||||
except:
|
except Exception as e:
|
||||||
raise UserDoesntExist(username, self.backend_name)
|
raise UserDoesntExist(username, self.backend_name)
|
||||||
|
|
||||||
def set_attrs(self, username, attrs):
|
def set_attrs(self, username, attrs):
|
||||||
""" Set a list of attributes for a given user
|
""" set a list of attributes for a given user
|
||||||
|
|
||||||
:param username: 'key' attribute of the user
|
:param username: 'key' attribute of the user
|
||||||
:type username: string
|
:type username: string
|
||||||
@ -128,7 +140,7 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
"""
|
"""
|
||||||
self._check_fix_users(username)
|
self._check_fix_users(username)
|
||||||
current_groups = self.users[username]['groups']
|
current_groups = self.users[username]['groups']
|
||||||
new_groups = current_groups | Set(groups)
|
new_groups = current_groups | set(groups)
|
||||||
self.users[username]['groups'] = new_groups
|
self.users[username]['groups'] = new_groups
|
||||||
|
|
||||||
def del_from_groups(self, username, groups):
|
def del_from_groups(self, username, groups):
|
||||||
@ -143,7 +155,7 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
"""
|
"""
|
||||||
self._check_fix_users(username)
|
self._check_fix_users(username)
|
||||||
current_groups = self.users[username]['groups']
|
current_groups = self.users[username]['groups']
|
||||||
new_groups = current_groups - Set(groups)
|
new_groups = current_groups - set(groups)
|
||||||
self.users[username]['groups'] = new_groups
|
self.users[username]['groups'] = new_groups
|
||||||
|
|
||||||
def search(self, searchstring):
|
def search(self, searchstring):
|
||||||
@ -176,7 +188,7 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return self.users[username]
|
return self.users[username]
|
||||||
except:
|
except Exception as e:
|
||||||
raise UserDoesntExist(username, self.backend_name)
|
raise UserDoesntExist(username, self.backend_name)
|
||||||
|
|
||||||
def get_groups(self, username):
|
def get_groups(self, username):
|
||||||
@ -188,5 +200,5 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return self.users[username]['groups']
|
return self.users[username]['groups']
|
||||||
except:
|
except Exception as e:
|
||||||
raise UserDoesntExist(username, self.backend_name)
|
raise UserDoesntExist(username, self.backend_name)
|
||||||
|
@ -11,11 +11,16 @@ import ldap.modlist as modlist
|
|||||||
import ldap.filter
|
import ldap.filter
|
||||||
import logging
|
import logging
|
||||||
import ldapcherry.backend
|
import ldapcherry.backend
|
||||||
|
import sys
|
||||||
from ldapcherry.exceptions import UserDoesntExist, \
|
from ldapcherry.exceptions import UserDoesntExist, \
|
||||||
GroupDoesntExist, \
|
GroupDoesntExist, \
|
||||||
UserAlreadyExists
|
UserAlreadyExists
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
if sys.version < '3':
|
||||||
|
from sets import Set as set
|
||||||
|
|
||||||
|
PYTHON_LDAP_MAJOR_VERSION = ldap.__version__[0]
|
||||||
|
|
||||||
|
|
||||||
class CaFileDontExist(Exception):
|
class CaFileDontExist(Exception):
|
||||||
@ -23,6 +28,21 @@ class CaFileDontExist(Exception):
|
|||||||
self.cafile = cafile
|
self.cafile = cafile
|
||||||
self.log = "CA file %(cafile)s does not exist" % {'cafile': cafile}
|
self.log = "CA file %(cafile)s does not exist" % {'cafile': cafile}
|
||||||
|
|
||||||
|
|
||||||
|
class MissingGroupAttr(Exception):
|
||||||
|
def __init__(self, attr):
|
||||||
|
self.attr = attr
|
||||||
|
self.log = "User doesn't have %(attr)s in its attributes" \
|
||||||
|
", cannot use it to set group" % {'attr': attr}
|
||||||
|
|
||||||
|
|
||||||
|
class MultivaluedGroupAttr(Exception):
|
||||||
|
def __init__(self, attr):
|
||||||
|
self.attr = cafile
|
||||||
|
self.log = "User's attribute '%(attr)s' is multivalued" \
|
||||||
|
", cannot use it to set group" % {'attr': attr}
|
||||||
|
|
||||||
|
|
||||||
NO_ATTR = 0
|
NO_ATTR = 0
|
||||||
DISPLAYED_ATTRS = 1
|
DISPLAYED_ATTRS = 1
|
||||||
LISTED_ATTRS = 2
|
LISTED_ATTRS = 2
|
||||||
@ -55,17 +75,21 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
self.key = key
|
self.key = key
|
||||||
# objectclasses parameter is a coma separated list in configuration
|
# objectclasses parameter is a coma separated list in configuration
|
||||||
# split it to get a real list, and convert it to bytes
|
# split it to get a real list, and convert it to bytes
|
||||||
for o in re.split('\W+', self.get_param('objectclasses')):
|
for o in re.split(r'\W+', self.get_param('objectclasses')):
|
||||||
self.objectclasses.append(self._str(o))
|
self.objectclasses.append(self._byte_p23(o))
|
||||||
self.group_attrs = {}
|
self.group_attrs = {}
|
||||||
|
self.group_attrs_keys = set([])
|
||||||
for param in config:
|
for param in config:
|
||||||
name, sep, group = param.partition('.')
|
name, sep, group = param.partition('.')
|
||||||
if name == 'group_attr':
|
if name == 'group_attr':
|
||||||
self.group_attrs[group] = self.get_param(param)
|
self.group_attrs[group] = self.get_param(param)
|
||||||
|
self.group_attrs_keys |= set(
|
||||||
|
self._extract_format_keys(self.get_param(param))
|
||||||
|
)
|
||||||
|
|
||||||
self.attrlist = []
|
self.attrlist = []
|
||||||
for a in attrslist:
|
for a in attrslist:
|
||||||
self.attrlist.append(self._str(a))
|
self.attrlist.append(self._byte_p2(a))
|
||||||
|
|
||||||
# exception handler (mainly to log something meaningful)
|
# exception handler (mainly to log something meaningful)
|
||||||
def _exception_handler(self, e):
|
def _exception_handler(self, e):
|
||||||
@ -108,8 +132,8 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
".groupdn'",
|
".groupdn'",
|
||||||
)
|
)
|
||||||
elif et is ldap.OBJECT_CLASS_VIOLATION:
|
elif et is ldap.OBJECT_CLASS_VIOLATION:
|
||||||
info = e[0]['info']
|
info = e.args[0]['info']
|
||||||
desc = e[0]['desc']
|
desc = e.args[0]['desc']
|
||||||
self._logger(
|
self._logger(
|
||||||
severity=logging.ERROR,
|
severity=logging.ERROR,
|
||||||
msg="Configuration error, " + desc + ", " + info,
|
msg="Configuration error, " + desc + ", " + info,
|
||||||
@ -123,7 +147,7 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
self.backend_name,
|
self.backend_name,
|
||||||
)
|
)
|
||||||
elif et is ldap.ALREADY_EXISTS:
|
elif et is ldap.ALREADY_EXISTS:
|
||||||
desc = e[0]['desc']
|
desc = e.args[0]['desc']
|
||||||
self._logger(
|
self._logger(
|
||||||
severity=logging.ERROR,
|
severity=logging.ERROR,
|
||||||
msg="adding user failed, " + desc,
|
msg="adding user failed, " + desc,
|
||||||
@ -135,6 +159,38 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
)
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
def _extract_format_keys(self, fmt_string):
|
||||||
|
"""Extract the keys of a format string
|
||||||
|
(the 'stuff' in '%(stuff)s'
|
||||||
|
"""
|
||||||
|
|
||||||
|
class AccessSaver:
|
||||||
|
def __init__(self):
|
||||||
|
self.keys = []
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
self.keys.append(key)
|
||||||
|
|
||||||
|
a = AccessSaver()
|
||||||
|
fmt_string % a
|
||||||
|
|
||||||
|
return a.keys
|
||||||
|
|
||||||
|
def _normalize_group_attrs(self, attrs):
|
||||||
|
"""Normalize the attributes used to set groups
|
||||||
|
If it's a list of one element, it just become this
|
||||||
|
element.
|
||||||
|
It raises an error if the attribute doesn't exist
|
||||||
|
or if it's multivaluated.
|
||||||
|
"""
|
||||||
|
for key in self.group_attrs_keys:
|
||||||
|
if key not in attrs:
|
||||||
|
raise MissingGroupAttr(key)
|
||||||
|
if type(attrs[key]) is list and len(attrs[key]) == 1:
|
||||||
|
attrs[key] = attrs[key][0]
|
||||||
|
if type(attrs[key]) is list and len(attrs[key]) != 1:
|
||||||
|
raise MultivaluedGroupAttr(key)
|
||||||
|
|
||||||
def _connect(self):
|
def _connect(self):
|
||||||
"""Initialize an ldap client"""
|
"""Initialize an ldap client"""
|
||||||
ldap_client = ldap.initialize(self.uri)
|
ldap_client = ldap.initialize(self.uri)
|
||||||
@ -205,6 +261,16 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
else:
|
else:
|
||||||
attrlist = None
|
attrlist = None
|
||||||
|
|
||||||
|
self._logger(
|
||||||
|
severity=logging.DEBUG,
|
||||||
|
msg="%(backend)s: executing search "
|
||||||
|
"with filter '%(filter)s' in DN '%(dn)s'" % {
|
||||||
|
'backend': self.backend_name,
|
||||||
|
'dn': basedn,
|
||||||
|
'filter': self._uni(searchfilter)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# bind and search the ldap
|
# bind and search the ldap
|
||||||
ldap_client = self._bind()
|
ldap_client = self._bind()
|
||||||
try:
|
try:
|
||||||
@ -246,7 +312,7 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
user_filter = self.user_filter_tmpl % {
|
user_filter = self.user_filter_tmpl % {
|
||||||
'username': self._uni(username)
|
'username': self._uni(username)
|
||||||
}
|
}
|
||||||
r = self._search(self._str(user_filter), attrs, self.userdn)
|
r = self._search(self._byte_p2(user_filter), attrs, self.userdn)
|
||||||
|
|
||||||
if len(r) == 0:
|
if len(r) == 0:
|
||||||
return None
|
return None
|
||||||
@ -258,33 +324,88 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
else:
|
else:
|
||||||
dn_entry = r[0]
|
dn_entry = r[0]
|
||||||
return dn_entry
|
return dn_entry
|
||||||
|
|
||||||
# python-ldap talks in bytes,
|
# python-ldap talks in bytes,
|
||||||
# as the rest of ldapcherry talks in unicode utf-8:
|
# as the rest of ldapcherry talks in unicode utf-8:
|
||||||
# * everything passed to python-ldap must be converted to bytes
|
# * everything passed to python-ldap must be converted to bytes
|
||||||
# * everything coming from python-ldap must be converted to unicode
|
# * everything coming from python-ldap must be converted to unicode
|
||||||
|
#
|
||||||
def _str(self, s):
|
# The previous statement was true for python-ldap < version 3.X.
|
||||||
|
# With versions > 3.0.0 and python 3, it gets tricky,
|
||||||
|
# some parts of python-ldap takes string, specially the filters/escaper.
|
||||||
|
#
|
||||||
|
# so we have now:
|
||||||
|
# *_byte_p2 (unicode -> bytes conversion for python 2)
|
||||||
|
# *_byte_p3 (unicode -> bytes conversion for python 3)
|
||||||
|
# *_byte_p23 (unicode -> bytes conversion for python AND 3)
|
||||||
|
def _byte_p23(self, s):
|
||||||
"""unicode -> bytes conversion"""
|
"""unicode -> bytes conversion"""
|
||||||
if s is None:
|
if s is None:
|
||||||
return None
|
return None
|
||||||
return s.encode('utf-8')
|
return s.encode('utf-8')
|
||||||
|
|
||||||
def _uni(self, s):
|
if sys.version < '3':
|
||||||
"""bytes -> unicode conversion"""
|
def _byte_p2(self, s):
|
||||||
if s is None:
|
"""unicode -> bytes conversion (python 2)"""
|
||||||
return None
|
if s is None:
|
||||||
return s.decode('utf-8', 'ignore')
|
return None
|
||||||
|
return s.encode('utf-8')
|
||||||
|
|
||||||
|
def _byte_p3(self, s):
|
||||||
|
"""pass through (does something in python 3)"""
|
||||||
|
return s
|
||||||
|
|
||||||
|
def _uni(self, s):
|
||||||
|
"""bytes -> unicode conversion"""
|
||||||
|
if s is None:
|
||||||
|
return None
|
||||||
|
return s.decode('utf-8', 'ignore')
|
||||||
|
|
||||||
|
def attrs_pretreatment(self, attrs):
|
||||||
|
attrs_srt = {}
|
||||||
|
for a in attrs:
|
||||||
|
attrs_srt[self._byte_p2(a)] = self._modlist(
|
||||||
|
self._byte_p2(attrs[a])
|
||||||
|
)
|
||||||
|
return attrs_srt
|
||||||
|
else:
|
||||||
|
def _byte_p2(self, s):
|
||||||
|
"""pass through (does something in python 2)"""
|
||||||
|
return s
|
||||||
|
|
||||||
|
def _byte_p3(self, s):
|
||||||
|
"""unicode -> bytes conversion"""
|
||||||
|
if s is None:
|
||||||
|
return None
|
||||||
|
return s.encode('utf-8')
|
||||||
|
|
||||||
|
def _uni(self, s):
|
||||||
|
"""bytes -> unicode conversion"""
|
||||||
|
if s is None:
|
||||||
|
return None
|
||||||
|
if type(s) is not str:
|
||||||
|
return s.decode('utf-8', 'ignore')
|
||||||
|
else:
|
||||||
|
return s
|
||||||
|
|
||||||
|
def attrs_pretreatment(self, attrs):
|
||||||
|
attrs_srt = {}
|
||||||
|
for a in attrs:
|
||||||
|
attrs_srt[self._byte_p2(a)] = self._modlist(
|
||||||
|
self._byte_p3(attrs[a])
|
||||||
|
)
|
||||||
|
return attrs_srt
|
||||||
|
|
||||||
def auth(self, username, password):
|
def auth(self, username, password):
|
||||||
"""Authentication of a user"""
|
"""Authentication of a user"""
|
||||||
|
|
||||||
binddn = self._get_user(self._str(username), NO_ATTR)
|
binddn = self._get_user(self._byte_p2(username), NO_ATTR)
|
||||||
if binddn is not None:
|
if binddn is not None:
|
||||||
ldap_client = self._connect()
|
ldap_client = self._connect()
|
||||||
try:
|
try:
|
||||||
ldap_client.simple_bind_s(
|
ldap_client.simple_bind_s(
|
||||||
self._str(binddn),
|
self._byte_p2(binddn),
|
||||||
self._str(password)
|
self._byte_p2(password)
|
||||||
)
|
)
|
||||||
except ldap.INVALID_CREDENTIALS:
|
except ldap.INVALID_CREDENTIALS:
|
||||||
ldap_client.unbind_s()
|
ldap_client.unbind_s()
|
||||||
@ -294,28 +415,35 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def attrs_pretreatment(self, attrs):
|
if PYTHON_LDAP_MAJOR_VERSION == '2':
|
||||||
attrs_str = {}
|
@staticmethod
|
||||||
for a in attrs:
|
def _modlist(in_attr):
|
||||||
attrs_str[self._str(a)] = self._str(attrs[a])
|
return in_attr
|
||||||
return attrs_str
|
|
||||||
|
else:
|
||||||
|
@staticmethod
|
||||||
|
def _modlist(in_attr):
|
||||||
|
return [in_attr]
|
||||||
|
|
||||||
def add_user(self, attrs):
|
def add_user(self, attrs):
|
||||||
"""add a user"""
|
"""add a user"""
|
||||||
ldap_client = self._bind()
|
ldap_client = self._bind()
|
||||||
# encoding crap
|
# encoding crap
|
||||||
attrs_str = self.attrs_pretreatment(attrs)
|
attrs_srt = self.attrs_pretreatment(attrs)
|
||||||
|
|
||||||
attrs_str['objectClass'] = self.objectclasses
|
attrs_srt[self._byte_p2('objectClass')] = self.objectclasses
|
||||||
# construct is DN
|
# construct is DN
|
||||||
dn = \
|
dn = \
|
||||||
self._str(self.dn_user_attr) + \
|
self._byte_p2(self.dn_user_attr) + \
|
||||||
'=' + \
|
self._byte_p2('=') + \
|
||||||
self._str(attrs[self.dn_user_attr]) + \
|
self._byte_p2(ldap.dn.escape_dn_chars(
|
||||||
',' + \
|
attrs[self.dn_user_attr]
|
||||||
self._str(self.userdn)
|
)
|
||||||
# gen the ldif fir add_s and add the user
|
) + \
|
||||||
ldif = modlist.addModlist(attrs_str)
|
self._byte_p2(',') + \
|
||||||
|
self._byte_p2(self.userdn)
|
||||||
|
# gen the ldif first add_s and add the user
|
||||||
|
ldif = modlist.addModlist(attrs_srt)
|
||||||
try:
|
try:
|
||||||
ldap_client.add_s(dn, ldif)
|
ldap_client.add_s(dn, ldif)
|
||||||
except ldap.ALREADY_EXISTS as e:
|
except ldap.ALREADY_EXISTS as e:
|
||||||
@ -329,7 +457,7 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
"""delete a user"""
|
"""delete a user"""
|
||||||
ldap_client = self._bind()
|
ldap_client = self._bind()
|
||||||
# recover the user dn
|
# recover the user dn
|
||||||
dn = self._str(self._get_user(self._str(username), NO_ATTR))
|
dn = self._byte_p2(self._get_user(self._byte_p2(username), NO_ATTR))
|
||||||
# delete
|
# delete
|
||||||
if dn is not None:
|
if dn is not None:
|
||||||
ldap_client.delete_s(dn)
|
ldap_client.delete_s(dn)
|
||||||
@ -339,15 +467,17 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
ldap_client.unbind_s()
|
ldap_client.unbind_s()
|
||||||
|
|
||||||
def set_attrs(self, username, attrs):
|
def set_attrs(self, username, attrs):
|
||||||
""" Set user attributes"""
|
""" set user attributes"""
|
||||||
ldap_client = self._bind()
|
ldap_client = self._bind()
|
||||||
tmp = self._get_user(self._str(username), ALL_ATTRS)
|
tmp = self._get_user(self._byte_p2(username), ALL_ATTRS)
|
||||||
dn = self._str(tmp[0])
|
if tmp is None:
|
||||||
|
raise UserDoesntExist(username, self.backend_name)
|
||||||
|
dn = self._byte_p2(tmp[0])
|
||||||
old_attrs = tmp[1]
|
old_attrs = tmp[1]
|
||||||
for attr in attrs:
|
for attr in attrs:
|
||||||
bcontent = self._str(attrs[attr])
|
bcontent = self._byte_p2(attrs[attr])
|
||||||
battr = self._str(attr)
|
battr = self._byte_p2(attr)
|
||||||
new = {battr: bcontent}
|
new = {battr: self._modlist(self._byte_p3(bcontent))}
|
||||||
# if attr is dn entry, use rename
|
# if attr is dn entry, use rename
|
||||||
if attr.lower() == self.dn_user_attr.lower():
|
if attr.lower() == self.dn_user_attr.lower():
|
||||||
ldap_client.rename_s(
|
ldap_client.rename_s(
|
||||||
@ -364,38 +494,42 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
if type(old_attrs[attr]) is list:
|
if type(old_attrs[attr]) is list:
|
||||||
tmp = []
|
tmp = []
|
||||||
for value in old_attrs[attr]:
|
for value in old_attrs[attr]:
|
||||||
tmp.append(self._str(value))
|
tmp.append(self._byte_p2(value))
|
||||||
bold_value = tmp
|
bold_value = tmp
|
||||||
else:
|
else:
|
||||||
bold_value = self._str(old_attrs[attr])
|
bold_value = self._modlist(
|
||||||
|
self._byte_p3(old_attrs[attr])
|
||||||
|
)
|
||||||
old = {battr: bold_value}
|
old = {battr: bold_value}
|
||||||
# attribute is not set, just add it
|
# attribute is not set, just add it
|
||||||
else:
|
else:
|
||||||
old = {}
|
old = {}
|
||||||
ldif = modlist.modifyModlist(old, new)
|
ldif = modlist.modifyModlist(old, new)
|
||||||
try:
|
if ldif:
|
||||||
ldap_client.modify_s(dn, ldif)
|
try:
|
||||||
except Exception as e:
|
ldap_client.modify_s(dn, ldif)
|
||||||
ldap_client.unbind_s()
|
except Exception as e:
|
||||||
self._exception_handler(e)
|
ldap_client.unbind_s()
|
||||||
|
self._exception_handler(e)
|
||||||
|
|
||||||
ldap_client.unbind_s()
|
ldap_client.unbind_s()
|
||||||
|
|
||||||
def add_to_groups(self, username, groups):
|
def add_to_groups(self, username, groups):
|
||||||
ldap_client = self._bind()
|
ldap_client = self._bind()
|
||||||
# recover dn of the user and his attributes
|
# recover dn of the user and his attributes
|
||||||
tmp = self._get_user(self._str(username), ALL_ATTRS)
|
tmp = self._get_user(self._byte_p2(username), ALL_ATTRS)
|
||||||
dn = tmp[0]
|
dn = tmp[0]
|
||||||
attrs = tmp[1]
|
attrs = tmp[1]
|
||||||
attrs['dn'] = dn
|
attrs['dn'] = dn
|
||||||
dn = self._str(tmp[0])
|
self._normalize_group_attrs(attrs)
|
||||||
|
dn = self._byte_p2(tmp[0])
|
||||||
# add user to all groups
|
# add user to all groups
|
||||||
for group in groups:
|
for group in groups:
|
||||||
group = self._str(group)
|
group = self._byte_p2(group)
|
||||||
# iterate on group membership attributes
|
# iterate on group membership attributes
|
||||||
for attr in self.group_attrs:
|
for attr in self.group_attrs:
|
||||||
# fill the content template
|
# fill the content template
|
||||||
content = self._str(self.group_attrs[attr] % attrs)
|
content = self._byte_p2(self.group_attrs[attr] % attrs)
|
||||||
self._logger(
|
self._logger(
|
||||||
severity=logging.DEBUG,
|
severity=logging.DEBUG,
|
||||||
msg="%(backend)s: adding user '%(user)s'"
|
msg="%(backend)s: adding user '%(user)s'"
|
||||||
@ -409,11 +543,14 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
'backend': self.backend_name
|
'backend': self.backend_name
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
ldif = modlist.modifyModlist({}, {attr: content})
|
ldif = modlist.modifyModlist(
|
||||||
|
{},
|
||||||
|
{attr: self._modlist(self._byte_p3(content))}
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
ldap_client.modify_s(group, ldif)
|
ldap_client.modify_s(group, ldif)
|
||||||
# if already member, not a big deal, just log it and continue
|
# if already member, not a big deal, just log it and continue
|
||||||
except ldap.TYPE_OR_VALUE_EXISTS as e:
|
except (ldap.TYPE_OR_VALUE_EXISTS, ldap.ALREADY_EXISTS) as e:
|
||||||
self._logger(
|
self._logger(
|
||||||
severity=logging.INFO,
|
severity=logging.INFO,
|
||||||
msg="%(backend)s: user '%(user)s'"
|
msg="%(backend)s: user '%(user)s'"
|
||||||
@ -437,16 +574,19 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
# it follows the same logic than add_to_groups
|
# it follows the same logic than add_to_groups
|
||||||
# but with MOD_DELETE
|
# but with MOD_DELETE
|
||||||
ldap_client = self._bind()
|
ldap_client = self._bind()
|
||||||
tmp = self._get_user(self._str(username), ALL_ATTRS)
|
tmp = self._get_user(self._byte_p2(username), ALL_ATTRS)
|
||||||
|
if tmp is None:
|
||||||
|
raise UserDoesntExist(username, self.backend_name)
|
||||||
dn = tmp[0]
|
dn = tmp[0]
|
||||||
attrs = tmp[1]
|
attrs = tmp[1]
|
||||||
attrs['dn'] = dn
|
attrs['dn'] = dn
|
||||||
dn = self._str(tmp[0])
|
self._normalize_group_attrs(attrs)
|
||||||
|
dn = self._byte_p2(tmp[0])
|
||||||
for group in groups:
|
for group in groups:
|
||||||
group = self._str(group)
|
group = self._byte_p2(group)
|
||||||
for attr in self.group_attrs:
|
for attr in self.group_attrs:
|
||||||
content = self._str(self.group_attrs[attr] % attrs)
|
content = self._byte_p2(self.group_attrs[attr] % attrs)
|
||||||
ldif = [(ldap.MOD_DELETE, attr, content)]
|
ldif = [(ldap.MOD_DELETE, attr, self._byte_p3(content))]
|
||||||
try:
|
try:
|
||||||
ldap_client.modify_s(group, ldif)
|
ldap_client.modify_s(group, ldif)
|
||||||
except ldap.NO_SUCH_ATTRIBUTE as e:
|
except ldap.NO_SUCH_ATTRIBUTE as e:
|
||||||
@ -469,7 +609,9 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
def search(self, searchstring):
|
def search(self, searchstring):
|
||||||
"""Search users"""
|
"""Search users"""
|
||||||
# escape special char to avoid injection
|
# escape special char to avoid injection
|
||||||
searchstring = ldap.filter.escape_filter_chars(self._str(searchstring))
|
searchstring = ldap.filter.escape_filter_chars(
|
||||||
|
self._byte_p2(searchstring)
|
||||||
|
)
|
||||||
# fill the search string template
|
# fill the search string template
|
||||||
searchfilter = self.search_filter_tmpl % {
|
searchfilter = self.search_filter_tmpl % {
|
||||||
'searchstring': searchstring
|
'searchstring': searchstring
|
||||||
@ -494,7 +636,7 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
def get_user(self, username):
|
def get_user(self, username):
|
||||||
"""Gest a specific user"""
|
"""Gest a specific user"""
|
||||||
ret = {}
|
ret = {}
|
||||||
tmp = self._get_user(self._str(username), ALL_ATTRS)
|
tmp = self._get_user(self._byte_p2(username), ALL_ATTRS)
|
||||||
if tmp is None:
|
if tmp is None:
|
||||||
raise UserDoesntExist(username, self.backend_name)
|
raise UserDoesntExist(username, self.backend_name)
|
||||||
attrs_tmp = tmp[1]
|
attrs_tmp = tmp[1]
|
||||||
@ -508,7 +650,7 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
|
|
||||||
def get_groups(self, username):
|
def get_groups(self, username):
|
||||||
"""Get all groups of a user"""
|
"""Get all groups of a user"""
|
||||||
username = ldap.filter.escape_filter_chars(self._str(username))
|
username = ldap.filter.escape_filter_chars(self._byte_p2(username))
|
||||||
userdn = self._get_user(username, NO_ATTR)
|
userdn = self._get_user(username, NO_ATTR)
|
||||||
|
|
||||||
searchfilter = self.group_filter_tmpl % {
|
searchfilter = self.group_filter_tmpl % {
|
||||||
@ -519,5 +661,5 @@ class Backend(ldapcherry.backend.Backend):
|
|||||||
groups = self._search(searchfilter, NO_ATTR, self.groupdn)
|
groups = self._search(searchfilter, NO_ATTR, self.groupdn)
|
||||||
ret = []
|
ret = []
|
||||||
for entry in groups:
|
for entry in groups:
|
||||||
ret.append(entry[0])
|
ret.append(self._uni(entry[0]))
|
||||||
return ret
|
return ret
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# vim:set expandtab tabstop=4 shiftwidth=4:
|
# vim:set expandtab tabstop=4 shiftwidth=4:
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
@ -95,14 +95,14 @@ def start(configfile=None, daemonize=False, environment=None,
|
|||||||
# Always start the engine; this will start all other services
|
# Always start the engine; this will start all other services
|
||||||
try:
|
try:
|
||||||
engine.start()
|
engine.start()
|
||||||
except:
|
except Exception as e:
|
||||||
# Assume the error has been logged already via bus.log.
|
# Assume the error has been logged already via bus.log.
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
engine.block()
|
engine.block()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
def main():
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
|
|
||||||
p = OptionParser()
|
p = OptionParser()
|
||||||
@ -142,3 +142,7 @@ if __name__ == '__main__':
|
|||||||
start(options.config, options.daemonize,
|
start(options.config, options.daemonize,
|
||||||
options.environment, options.fastcgi, options.scgi,
|
options.environment, options.fastcgi, options.scgi,
|
||||||
options.pidfile, options.cgi, options.debug)
|
options.pidfile, options.cgi, options.debug)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
@ -46,11 +46,12 @@ class MissingRole(Exception):
|
|||||||
|
|
||||||
|
|
||||||
class MissingBackend(Exception):
|
class MissingBackend(Exception):
|
||||||
def __init__(self, backend):
|
def __init__(self, backend, type_conf):
|
||||||
self.backend = backend
|
self.backend = backend
|
||||||
self.log = \
|
self.log = \
|
||||||
"backend '%(backend)s' does not exist in main config file" % \
|
"backend '%(backend)s' does not exist in main config file " \
|
||||||
{'backend': backend}
|
"but is still declared in '%(type_conf)s' file" % \
|
||||||
|
{'backend': backend, 'type_conf': type_conf}
|
||||||
|
|
||||||
|
|
||||||
class WrongBackend(Exception):
|
class WrongBackend(Exception):
|
||||||
@ -90,10 +91,10 @@ class PPolicyError(Exception):
|
|||||||
|
|
||||||
class MissingMainFile(Exception):
|
class MissingMainFile(Exception):
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.rolefile = rolefile
|
self.config = config
|
||||||
self.log = \
|
self.log = \
|
||||||
"fail to open main file '%(config)s'" % \
|
"fail to open main file '%(config)s'" % \
|
||||||
{'rolefile': rolefile}
|
{'config': config}
|
||||||
|
|
||||||
|
|
||||||
class MissingAttributesFile(Exception):
|
class MissingAttributesFile(Exception):
|
||||||
@ -217,6 +218,11 @@ class GroupDoesntExist(Exception):
|
|||||||
" in backend '" + backend + "'"
|
" in backend '" + backend + "'"
|
||||||
|
|
||||||
|
|
||||||
|
class TemplateRenderError(Exception):
|
||||||
|
def __init__(self, error):
|
||||||
|
self.log = "Template Render Error: " + error
|
||||||
|
|
||||||
|
|
||||||
def exception_decorator(func):
|
def exception_decorator(func):
|
||||||
def ret(self, *args, **kwargs):
|
def ret(self, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# vim:set expandtab tabstop=4 shiftwidth=4:
|
# vim:set expandtab tabstop=4 shiftwidth=4:
|
||||||
#
|
#
|
||||||
|
@ -31,9 +31,9 @@ class PPolicy(ldapcherry.ppolicy.PPolicy):
|
|||||||
|
|
||||||
def info(self):
|
def info(self):
|
||||||
return \
|
return \
|
||||||
"* Minimum length: %(len)n\n" \
|
"* Minimum length: %(len)d\n" \
|
||||||
"* Minimum number of uppercase characters: %(upper)n\n" \
|
"* Minimum number of uppercase characters: %(upper)d\n" \
|
||||||
"* Minimum number of digits: %(digit)n" % {
|
"* Minimum number of digits: %(digit)d" % {
|
||||||
'upper': self.min_upper,
|
'upper': self.min_upper,
|
||||||
'len': self.min_length,
|
'len': self.min_length,
|
||||||
'digit': self.min_digit
|
'digit': self.min_digit
|
||||||
|
@ -9,12 +9,14 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
from sets import Set
|
|
||||||
from ldapcherry.pyyamlwrapper import loadNoDump
|
from ldapcherry.pyyamlwrapper import loadNoDump
|
||||||
from ldapcherry.pyyamlwrapper import DumplicatedKey
|
from ldapcherry.pyyamlwrapper import DumplicatedKey
|
||||||
from ldapcherry.exceptions import *
|
from ldapcherry.exceptions import *
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
if sys.version < '3':
|
||||||
|
from sets import Set as set
|
||||||
|
|
||||||
|
|
||||||
class CustomDumper(yaml.SafeDumper):
|
class CustomDumper(yaml.SafeDumper):
|
||||||
"A custom YAML dumper that never emits aliases"
|
"A custom YAML dumper that never emits aliases"
|
||||||
@ -27,10 +29,10 @@ class Roles:
|
|||||||
|
|
||||||
def __init__(self, role_file):
|
def __init__(self, role_file):
|
||||||
self.role_file = role_file
|
self.role_file = role_file
|
||||||
self.backends = Set([])
|
self.backends = set([])
|
||||||
try:
|
try:
|
||||||
stream = open(role_file, 'r')
|
stream = open(role_file, 'r')
|
||||||
except:
|
except Exception as e:
|
||||||
raise MissingRolesFile(role_file)
|
raise MissingRolesFile(role_file)
|
||||||
try:
|
try:
|
||||||
self.roles_raw = loadNoDump(stream)
|
self.roles_raw = loadNoDump(stream)
|
||||||
@ -51,11 +53,12 @@ class Roles:
|
|||||||
for backends in backends_list:
|
for backends in backends_list:
|
||||||
for b in backends:
|
for b in backends:
|
||||||
if b not in ret:
|
if b not in ret:
|
||||||
ret[b] = Set([])
|
ret[b] = set([])
|
||||||
for group in backends[b]:
|
for group in backends[b]:
|
||||||
ret[b].add(group)
|
ret[b].add(group)
|
||||||
for b in ret:
|
for b in ret:
|
||||||
ret[b] = list(ret[b])
|
ret[b] = list(ret[b])
|
||||||
|
ret[b].sort()
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def _flatten(self, roles=None, groups=None):
|
def _flatten(self, roles=None, groups=None):
|
||||||
@ -134,8 +137,8 @@ class Roles:
|
|||||||
|
|
||||||
if roleid not in self.graph:
|
if roleid not in self.graph:
|
||||||
self.graph[roleid] = {
|
self.graph[roleid] = {
|
||||||
'parent_roles': Set([]),
|
'parent_roles': set([]),
|
||||||
'sub_roles': Set([])
|
'sub_roles': set([])
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create the nested groups
|
# Create the nested groups
|
||||||
@ -147,7 +150,7 @@ class Roles:
|
|||||||
if b not in self.group2roles:
|
if b not in self.group2roles:
|
||||||
self.group2roles[b] = {}
|
self.group2roles[b] = {}
|
||||||
if g not in self.group2roles[b]:
|
if g not in self.group2roles[b]:
|
||||||
self.group2roles[b][g] = Set([])
|
self.group2roles[b][g] = set([])
|
||||||
self.group2roles[b][g].add(roleid)
|
self.group2roles[b][g].add(roleid)
|
||||||
|
|
||||||
parent_roles[roleid] = []
|
parent_roles[roleid] = []
|
||||||
@ -223,7 +226,7 @@ class Roles:
|
|||||||
# add groups of the role to usedgroups
|
# add groups of the role to usedgroups
|
||||||
for b in self.roles[role]['backends_groups']:
|
for b in self.roles[role]['backends_groups']:
|
||||||
if b not in usedgroups:
|
if b not in usedgroups:
|
||||||
usedgroups[b] = Set([])
|
usedgroups[b] = set([])
|
||||||
for g in self.roles[role]['backends_groups'][b]:
|
for g in self.roles[role]['backends_groups'][b]:
|
||||||
usedgroups[b].add(g)
|
usedgroups[b].add(g)
|
||||||
|
|
||||||
@ -254,11 +257,11 @@ class Roles:
|
|||||||
"""get groups to remove from list of
|
"""get groups to remove from list of
|
||||||
roles to remove and current roles
|
roles to remove and current roles
|
||||||
"""
|
"""
|
||||||
current_roles = Set(current_roles)
|
current_roles = set(current_roles)
|
||||||
|
|
||||||
ret = {}
|
ret = {}
|
||||||
roles_to_remove = Set(roles_to_remove)
|
roles_to_remove = set(roles_to_remove)
|
||||||
tmp = Set([])
|
tmp = set([])
|
||||||
# get sub roles of the role to remove that the user belongs to
|
# get sub roles of the role to remove that the user belongs to
|
||||||
# if we remove a role, there is no reason to keep the sub roles
|
# if we remove a role, there is no reason to keep the sub roles
|
||||||
for r in roles_to_remove:
|
for r in roles_to_remove:
|
||||||
@ -267,7 +270,7 @@ class Roles:
|
|||||||
tmp.add(sr)
|
tmp.add(sr)
|
||||||
|
|
||||||
roles_to_remove = roles_to_remove.union(tmp)
|
roles_to_remove = roles_to_remove.union(tmp)
|
||||||
roles = current_roles.difference(Set(roles_to_remove))
|
roles = current_roles.difference(set(roles_to_remove))
|
||||||
groups_roles = self._get_groups(roles)
|
groups_roles = self._get_groups(roles)
|
||||||
groups_roles_to_remove = self._get_groups(roles_to_remove)
|
groups_roles_to_remove = self._get_groups(roles_to_remove)
|
||||||
|
|
||||||
@ -284,12 +287,12 @@ class Roles:
|
|||||||
for b in self.flatten[r]['backends_groups']:
|
for b in self.flatten[r]['backends_groups']:
|
||||||
groups = self.flatten[r]['backends_groups'][b]
|
groups = self.flatten[r]['backends_groups'][b]
|
||||||
if b not in ret:
|
if b not in ret:
|
||||||
ret[b] = Set(groups)
|
ret[b] = set(groups)
|
||||||
ret[b] = ret[b].union(Set(groups))
|
ret[b] = ret[b].union(set(groups))
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def _get_subroles(self, role):
|
def _get_subroles(self, role):
|
||||||
ret = Set([])
|
ret = set([])
|
||||||
for sr in self.graph[role]['sub_roles']:
|
for sr in self.graph[role]['sub_roles']:
|
||||||
tmp = self._get_subroles(sr)
|
tmp = self._get_subroles(sr)
|
||||||
tmp.add(sr)
|
tmp.add(sr)
|
||||||
@ -298,10 +301,10 @@ class Roles:
|
|||||||
|
|
||||||
def get_roles(self, groups):
|
def get_roles(self, groups):
|
||||||
"""get list of roles and list of standalone groups"""
|
"""get list of roles and list of standalone groups"""
|
||||||
roles = Set([])
|
roles = set([])
|
||||||
parentroles = Set([])
|
parentroles = set([])
|
||||||
notroles = Set([])
|
notroles = set([])
|
||||||
tmp = Set([])
|
tmp = set([])
|
||||||
usedgroups = {}
|
usedgroups = {}
|
||||||
unusedgroups = {}
|
unusedgroups = {}
|
||||||
ret = {}
|
ret = {}
|
||||||
@ -316,7 +319,7 @@ class Roles:
|
|||||||
for g in groups[b]:
|
for g in groups[b]:
|
||||||
if b not in usedgroups or g not in usedgroups[b]:
|
if b not in usedgroups or g not in usedgroups[b]:
|
||||||
if b not in unusedgroups:
|
if b not in unusedgroups:
|
||||||
unusedgroups[b] = Set([])
|
unusedgroups[b] = set([])
|
||||||
unusedgroups[b].add(g)
|
unusedgroups[b].add(g)
|
||||||
|
|
||||||
ret['roles'] = roles
|
ret['roles'] = roles
|
||||||
|
8
ldapcherry/version.py
Normal file
8
ldapcherry/version.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim:set expandtab tabstop=4 shiftwidth=4:
|
||||||
|
#
|
||||||
|
# The MIT License (MIT)
|
||||||
|
# ldapCherry
|
||||||
|
# Copyright (c) 2014 Carpentier Pierre-Francois
|
||||||
|
|
||||||
|
version = '1.1.1'
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
@ -8,7 +8,6 @@ import pytest
|
|||||||
import sys
|
import sys
|
||||||
from sets import Set
|
from sets import Set
|
||||||
from ldapcherry.backend.backendLdap import Backend
|
from ldapcherry.backend.backendLdap import Backend
|
||||||
from ldapcherry import syslog_error
|
|
||||||
from ldapcherry.exceptions import *
|
from ldapcherry.exceptions import *
|
||||||
import cherrypy
|
import cherrypy
|
||||||
import logging
|
import logging
|
||||||
|
@ -30,7 +30,7 @@ groups = {
|
|||||||
'ad' : ['Domain Users', 'Domain Users 2'],
|
'ad' : ['Domain Users', 'Domain Users 2'],
|
||||||
'ldap': ['cn=users,ou=group,dc=example,dc=com',
|
'ldap': ['cn=users,ou=group,dc=example,dc=com',
|
||||||
'cn=nagios admins,ou=group,dc=example,dc=com',
|
'cn=nagios admins,ou=group,dc=example,dc=com',
|
||||||
'cn=developpers,ou=group,dc=example,dc=com',
|
'cn=developers,ou=group,dc=example,dc=com',
|
||||||
],
|
],
|
||||||
'toto': ['not a group'],
|
'toto': ['not a group'],
|
||||||
}
|
}
|
||||||
|
4
requirements-el7.txt
Normal file
4
requirements-el7.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
CherryPy>=3.0.0
|
||||||
|
PyYAML
|
||||||
|
Mako
|
||||||
|
python-ldap==3.4.0
|
4
requirements-stretch.txt
Normal file
4
requirements-stretch.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
CherryPy>=3.0.0
|
||||||
|
PyYAML
|
||||||
|
Mako
|
||||||
|
python-ldap==3.4.0
|
25
resources/static/js/alignforms.js
Normal file
25
resources/static/js/alignforms.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
var classes = ["lcform-col-1", "lcform-col-2"];
|
||||||
|
var j, lenj = classes.length;
|
||||||
|
for(j=0; j < lenj; j++){
|
||||||
|
var formSelector = classes[j];
|
||||||
|
var forms = document.getElementsByClassName(formSelector);
|
||||||
|
//console.log(formSelector);
|
||||||
|
//console.log(forms);
|
||||||
|
if (forms.length > 0){
|
||||||
|
forms[0].style.removeProperty("display");
|
||||||
|
var InputGroups = forms[0].getElementsByClassName("input-group-addon");
|
||||||
|
//console.log(InputGroups);
|
||||||
|
var i, len = InputGroups.length;
|
||||||
|
var longest = 0;
|
||||||
|
for(i=0; i < len; i++){
|
||||||
|
if (InputGroups[i].id !== "basic-addon-password2"){
|
||||||
|
longest = longest < InputGroups[i].clientWidth ? InputGroups[i].clientWidth : longest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(i=0; i < len; i++){
|
||||||
|
InputGroups[i].style.minWidth = (longest + 0) + "px";
|
||||||
|
}
|
||||||
|
//console.log(longest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//console.log("end_re");
|
@ -30,3 +30,8 @@ function lcUidNumber(firstname, lastname, minuid, maxuid){
|
|||||||
function lcHomeDir(firstname, lastname, basedir){
|
function lcHomeDir(firstname, lastname, basedir){
|
||||||
return basedir+lcUid(firstname, lastname);
|
return basedir+lcUid(firstname, lastname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function lcCopy(value){
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -10,9 +10,24 @@ $('#form').validator({
|
|||||||
type: 'POST',
|
type: 'POST',
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
async: false,
|
async: false,
|
||||||
data: 'pwd=' + $el.val(),
|
data: 'pwd=' + encodeURIComponent($el.val()),
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
$ret = data;
|
$ret = data;
|
||||||
|
},
|
||||||
|
error: function(jqXHR, exception) {
|
||||||
|
switch (jqXHR.status) {
|
||||||
|
case 400:
|
||||||
|
$ret = {"reason":"Javascript ppolicy.js error","match":false};
|
||||||
|
break;
|
||||||
|
case 403:
|
||||||
|
$ret = {"reason":"Session expired, you must reconnect","match":false};
|
||||||
|
break;
|
||||||
|
case 500:
|
||||||
|
$ret = {"reason":"Server error","match":false};
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$ret = {"reason":"Unknown error [" + jqXHR.status + "], check logs","match":false};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.options.errors['ppolicy'] = $ret['reason'];
|
this.options.errors['ppolicy'] = $ret['reason'];
|
||||||
|
@ -6,14 +6,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-12 column">
|
<div class="col-md-12 column">
|
||||||
<div class="well well-sm">
|
<div class="well well-sm">
|
||||||
<form method='POST' autocomplete="off" action='/adduser' role="form" class="form-signin" id=form>
|
<form method='POST' autocomplete="off" action='/adduser' role="form" class="form-signin" id="form">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Fill new user's attributes:</legend>
|
<legend>Fill new user's attributes:</legend>
|
||||||
${form}
|
${form | n}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Enable/Disable user's roles:</legend>
|
<legend>Enable/Disable user's roles:</legend>
|
||||||
${roles}
|
${roles | n}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
|
@ -58,7 +58,7 @@
|
|||||||
<body>
|
<body>
|
||||||
% if notifications:
|
% if notifications:
|
||||||
% for notif in notifications:
|
% for notif in notifications:
|
||||||
<script type="text/javascript">$.notify('${notif}')</script>
|
<script type="text/javascript">$.notify('${notif | n}')</script>
|
||||||
% endfor
|
% endfor
|
||||||
% endif
|
% endif
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@ -70,5 +70,6 @@
|
|||||||
<p class="muted credit"><a href="http://ldapcherry.readthedocs.org" target="_blank">LdapCherry</a> • © 2016 • Pierre-François Carpentier • Released under the MIT License</p>
|
<p class="muted credit"><a href="http://ldapcherry.readthedocs.org" target="_blank">LdapCherry</a> • © 2016 • Pierre-François Carpentier • Released under the MIT License</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script type="text/javascript" src="/static/js/alignforms.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
## -*- coding: utf-8 -*-
|
## -*- coding: utf-8 -*-
|
||||||
<%
|
<%
|
||||||
|
from markupsafe import Markup
|
||||||
len_attr = len(attributes)
|
len_attr = len(attributes)
|
||||||
switch = len_attr / 2
|
switch = len_attr / 2
|
||||||
if not switch * 2 == len_attr:
|
if not switch * 2 == len_attr:
|
||||||
@ -26,44 +27,49 @@ for a in sorted(attributes.keys(), key=lambda attr: attributes[attr]['weight']):
|
|||||||
required = ' required '
|
required = ' required '
|
||||||
if not values is None and a in values:
|
if not values is None and a in values:
|
||||||
if type(values[a]) is list:
|
if type(values[a]) is list:
|
||||||
tmp = values[a][0]
|
raw_value = values[a][0]
|
||||||
else:
|
else:
|
||||||
tmp = values[a]
|
raw_value = values[a]
|
||||||
if tmp is None:
|
if raw_value is None:
|
||||||
tmp = ''
|
raw_value = ''
|
||||||
value = ' value="'+ tmp + '"'
|
value = Markup(' value="{}"').format(raw_value)
|
||||||
value2 = '<option>'+ tmp +'</option>'
|
value2 = Markup('<option>{}</option>').format(raw_value)
|
||||||
else:
|
else:
|
||||||
|
raw_value = ''
|
||||||
value = ''
|
value = ''
|
||||||
value2 = ''
|
value2 = ''
|
||||||
if 'default' in attr and value == '':
|
if 'default' in attr and value == '':
|
||||||
value = ' value="'+ attr['default'] + '"'
|
value = Markup(' value="{}"').format(attr['default'])
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<span class="input-group-addon" id="basic-addon-${a}">${attr['display_name']}</span>
|
<span class="input-group-addon" id="basic-addon-${a}">${attr['display_name']}</span>
|
||||||
% if modify and a == keyattr:
|
% if modify and a == keyattr:
|
||||||
<input type="hidden" id="attr.${a}" name="attr.${a}" class="form-control" autocomplete='off' aria-describedby="basic-addon-${a}" ${required} ${value} readonly onfocus="this.removeAttribute('readonly');">
|
<input type="hidden" id="attr.${a}" name="attr.${a}" class="form-control" autocomplete='off' aria-describedby="basic-addon-${a}" ${required} ${value | n} readonly onfocus="this.removeAttribute('readonly');">
|
||||||
<span class="form-control" aria-describedby="basic-addon-${a}">${tmp}</span>
|
<span class="form-control" aria-describedby="basic-addon-${a}">${raw_value}</span>
|
||||||
% elif attr['type'] == 'string':
|
% elif attr['type'] == 'string':
|
||||||
<input type="text" id="attr.${a}" name="attr.${a}" class="form-control" autocomplete='off' placeholder="${attr['description']}" aria-describedby="basic-addon-${a}" ${required} ${value} readonly onfocus="this.removeAttribute('readonly');">
|
<input type="text" id="attr.${a}" name="attr.${a}" class="form-control" autocomplete='off' placeholder="${attr['description']}" aria-describedby="basic-addon-${a}" ${required} ${value | n} readonly onfocus="this.removeAttribute('readonly');">
|
||||||
% elif attr['type'] == 'email':
|
% elif attr['type'] == 'email':
|
||||||
<input type="email" id="attr.${a}" name="attr.${a}" class="form-control" autocomplete='off' placeholder="${attr['description']}" aria-describedby="basic-addon-${a}" ${required} ${value} data-error="email address is invalid" readonly onfocus="this.removeAttribute('readonly');">
|
<input type="email" id="attr.${a}" name="attr.${a}" class="form-control" autocomplete='off' placeholder="${attr['description']}" aria-describedby="basic-addon-${a}" ${required} ${value | n} data-error="email address is invalid" readonly onfocus="this.removeAttribute('readonly');">
|
||||||
% elif attr['type'] == 'int':
|
% elif attr['type'] == 'int':
|
||||||
<input type="number" id="attr.${a}" name="attr.${a}" class="form-control" autocomplete='off' placeholder="${attr['description']}" aria-describedby="basic-addon-${a}" ${required} ${value} readonly onfocus="this.removeAttribute('readonly');">
|
<input type="number" id="attr.${a}" name="attr.${a}" class="form-control" autocomplete='off' placeholder="${attr['description']}" aria-describedby="basic-addon-${a}" ${required} ${value | n} readonly onfocus="this.removeAttribute('readonly');">
|
||||||
% elif attr['type'] == 'fix':
|
% elif attr['type'] == 'fix':
|
||||||
<input type="hidden" id="attr.${a}" name="attr.${a}" class="form-control" autocomplete='off' aria-describedby="basic-addon-${a}" ${required} value="${attr['value']}" readonly onfocus="this.removeAttribute('readonly');">
|
<input type="hidden" id="attr.${a}" name="attr.${a}" class="form-control" autocomplete='off' aria-describedby="basic-addon-${a}" ${required} value="${attr['value']}" readonly onfocus="this.removeAttribute('readonly');">
|
||||||
<span class="form-control" placeholder="${attr['description']}" aria-describedby="basic-addon-${a}">${attr['value']}</span>
|
<span class="form-control" placeholder="${attr['description']}" aria-describedby="basic-addon-${a}">${attr['value']}</span>
|
||||||
% elif attr['type'] == 'stringlist':
|
% elif attr['type'] == 'stringlist':
|
||||||
<select class="form-control" id="attr.${a}" name="attr.${a}">
|
<select class="form-control" id="attr.${a}" name="attr.${a}">
|
||||||
${value2}
|
${value2 | n}
|
||||||
%for val in attr['values']:
|
%for val in attr['values']:
|
||||||
|
%if '<option>' + val + '</option>' != value2:
|
||||||
<option>${val}</option>
|
<option>${val}</option>
|
||||||
|
%endif
|
||||||
%endfor
|
%endfor
|
||||||
</select>
|
</select>
|
||||||
% elif attr['type'] == 'password':
|
% elif attr['type'] == 'password':
|
||||||
<input type="password" class="form-control" data-ppolicy="ppolicy" name="attr.${a}1" id="${a}1" autocomplete='off' placeholder="${attr['description']}" ${required} readonly onfocus="this.removeAttribute('readonly');">
|
<input type="password" class="form-control" data-ppolicy="ppolicy" name="attr.${a}1" id="${a}1" autocomplete='off' placeholder="${attr['description']}" ${required} readonly onfocus="this.removeAttribute('readonly');">
|
||||||
<span class="input-group-addon" id="basic-addon-${a}2">Retype ${attr['display_name']}</span>
|
<span class="input-group-addon" id="basic-addon-${a}2">Retype ${attr['display_name']}</span>
|
||||||
<input type="password" class="form-control" data-match="#${a}1" data-match-error="Passwords don't match" name="attr.${a}2" id="#${a}2" autocomplete='off' placeholder="Confirm" ${required} readonly onfocus="this.removeAttribute('readonly');">
|
<input type="password" class="form-control" data-match="#${a}1" data-match-error="Passwords don't match" name="attr.${a}2" id="#${a}2" autocomplete='off' placeholder="Confirm" ${required} readonly onfocus="this.removeAttribute('readonly');">
|
||||||
|
% elif attr['type'] == 'textfield':
|
||||||
|
<textarea id="attr.${a}" name="attr.${a}" class="form-control" placeholder="${attr['description']}">${raw_value}</textarea>
|
||||||
% endif
|
% endif
|
||||||
</div>
|
</div>
|
||||||
<div class="help-block with-errors"></div>
|
<div class="help-block with-errors"></div>
|
||||||
@ -71,39 +77,40 @@ for a in sorted(attributes.keys(), key=lambda attr: attributes[attr]['weight']):
|
|||||||
% endfor
|
% endfor
|
||||||
</%def>
|
</%def>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 column">
|
<div class="col-md-6 column lcform-col-1" style="display:none;">
|
||||||
${form_col(lc1)}
|
${form_col(lc1)}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 column">
|
<div class="col-md-6 column lcform-col-2" style="display:none;">
|
||||||
${form_col(lc2)}
|
${form_col(lc2)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
% if autofill:
|
% if autofill:
|
||||||
<%
|
<%
|
||||||
from sets import Set
|
attr_set = []
|
||||||
attr_set = Set([])
|
|
||||||
attr_events = {}
|
attr_events = {}
|
||||||
functions = {}
|
functions = {}
|
||||||
for attrid in attributes:
|
for attrid in attributes:
|
||||||
attr = attributes[attrid]
|
attr = attributes[attrid]
|
||||||
field = 'attr.' + attrid
|
field = 'attr.' + attrid
|
||||||
attr_set.add(field)
|
if field not in attr_set:
|
||||||
if 'autofill' in attr:
|
attr_set.append(field)
|
||||||
function = attr['autofill']['function']
|
if 'autofill' in attr:
|
||||||
tuple = (field, function)
|
function = attr['autofill']['function']
|
||||||
if not tuple in functions:
|
tuple = (field, function)
|
||||||
functions[tuple] = []
|
if not tuple in functions:
|
||||||
for arg in attr['autofill']['args']:
|
functions[tuple] = []
|
||||||
if arg[0] == '$':
|
for arg in attr['autofill']['args']:
|
||||||
field_arg = 'attr.' + arg[1:]
|
if arg[0] == '$':
|
||||||
attr_set.add(field_arg)
|
field_arg = 'attr.' + arg[1:]
|
||||||
functions[tuple].append("fields['" + field_arg + "'].value")
|
if field_arg not in attr_set:
|
||||||
if not field_arg in attr_events:
|
attr_set.append(field_arg)
|
||||||
attr_events[field_arg] = []
|
functions[tuple].append("fields['" + field_arg + "'].value")
|
||||||
attr_events[field_arg].append(tuple)
|
if not field_arg in attr_events:
|
||||||
else:
|
attr_events[field_arg] = []
|
||||||
value = arg
|
attr_events[field_arg].append(tuple)
|
||||||
functions[tuple].append("'" + value + "'")
|
else:
|
||||||
|
value = arg
|
||||||
|
functions[tuple].append("'" + value + "'")
|
||||||
%>
|
%>
|
||||||
<script>
|
<script>
|
||||||
var fields = new Object();
|
var fields = new Object();
|
||||||
@ -116,7 +123,7 @@ if (fields['${attrid}'] != null) {
|
|||||||
fields['${attrid}'].onchange = function () {
|
fields['${attrid}'].onchange = function () {
|
||||||
% for tuple in attr_events[attrid]:
|
% for tuple in attr_events[attrid]:
|
||||||
if (typeof(${tuple[1]}) == "function") {
|
if (typeof(${tuple[1]}) == "function") {
|
||||||
fields['${tuple[0]}'].value = ${tuple[1]}(${', '.join(functions[tuple])});
|
fields['${tuple[0]}'].value = ${tuple[1] | n}(${', '.join(functions[tuple]) | n});
|
||||||
}
|
}
|
||||||
% endfor
|
% endfor
|
||||||
};
|
};
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<table id="RecordTable" class="table table-hover table-condensed">
|
<table id="RecordTable" class="table table-hover table-condensed">
|
||||||
<tbody>
|
|
||||||
% if not searchresult is None:
|
% if not searchresult is None:
|
||||||
|
<tbody>
|
||||||
%for attr in sorted(attrs_list.keys(), key=lambda attr: attrs_list[attr]['weight']):
|
%for attr in sorted(attrs_list.keys(), key=lambda attr: attrs_list[attr]['weight']):
|
||||||
<tr>
|
<tr>
|
||||||
% if attr in searchresult:
|
% if attr in searchresult:
|
||||||
@ -26,8 +26,8 @@
|
|||||||
% endif
|
% endif
|
||||||
</tr>
|
</tr>
|
||||||
% endfor
|
% endfor
|
||||||
%endif
|
|
||||||
</tbody>
|
</tbody>
|
||||||
|
%endif
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,13 +4,13 @@
|
|||||||
<div class="row clearfix" style="margin-top:30px">
|
<div class="row clearfix" style="margin-top:30px">
|
||||||
<div class="col-md-4 column"></div>
|
<div class="col-md-4 column"></div>
|
||||||
<div class="col-md-4 column well">
|
<div class="col-md-4 column well">
|
||||||
<%
|
<form method='POST' role="form" class="form-signin"
|
||||||
if url is None:
|
% if url:
|
||||||
qs=''
|
action='login?url=${url | u}'
|
||||||
else:
|
% else:
|
||||||
qs='?url=' + url
|
action='login'
|
||||||
%>
|
% endif
|
||||||
<form method='POST' action='/login${qs}' role="form" class="form-signin">
|
>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<h2 class="form-signin-heading">Please sign in</h2>
|
<h2 class="form-signin-heading">Please sign in</h2>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
|
@ -9,11 +9,11 @@
|
|||||||
<form method='POST' action='/modify' role="form" class="form-signin" id="form">
|
<form method='POST' action='/modify' role="form" class="form-signin" id="form">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Modify user's attributes:</legend>
|
<legend>Modify user's attributes:</legend>
|
||||||
${form}
|
${form | n}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Enable/Disable user's roles:</legend>
|
<legend>Enable/Disable user's roles:</legend>
|
||||||
${roles}
|
${roles | n}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
% if len(standalone_groups) != 0:
|
% if len(standalone_groups) != 0:
|
||||||
<fieldset>
|
<fieldset>
|
||||||
|
@ -13,12 +13,19 @@
|
|||||||
<a class="navbar-brand" href="/searchadmin">Delete/Modify User</a>
|
<a class="navbar-brand" href="/searchadmin">Delete/Modify User</a>
|
||||||
% endif
|
% endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
||||||
<a class="navbar-brand navbar-right" href='/logout'><span class="glyphicon glyphicon-off"></span> Logout</a>
|
<a class="navbar-brand navbar-right" href='/logout'><span class="glyphicon glyphicon-off"></span> Logout</a>
|
||||||
|
% if is_admin:
|
||||||
|
<form method='GET' action='/searchadmin' class="navbar-form navbar-right" role="search" data-toggle="validator">
|
||||||
|
% else:
|
||||||
<form method='GET' action='/searchuser' class="navbar-form navbar-right" role="search" data-toggle="validator">
|
<form method='GET' action='/searchuser' class="navbar-form navbar-right" role="search" data-toggle="validator">
|
||||||
|
% endif
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
% if is_admin:
|
||||||
|
<input type="text" class="form-control" name="searchstring" placeholder="Search User">
|
||||||
|
% else:
|
||||||
<input type="text" class="form-control" data-minlength="3" name="searchstring" placeholder="Search User" data-error="Too short" required>
|
<input type="text" class="form-control" data-minlength="3" name="searchstring" placeholder="Search User" data-error="Too short" required>
|
||||||
|
% endif
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-default">Submit</button>
|
<button type="submit" class="btn btn-default">Submit</button>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
## -*- coding: utf-8 -*-
|
## -*- coding: utf-8 -*-
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var graph = ${graph_js};
|
var graph = ${graph_js | n};
|
||||||
var roles = ${roles_js};
|
var roles = ${roles_js | n};
|
||||||
function enableParentRoles(roleid){
|
function enableParentRoles(roleid){
|
||||||
var parentRoles = graph[roleid]['parent_roles'];
|
var parentRoles = graph[roleid]['parent_roles'];
|
||||||
var DnRole = roles[roleid];
|
var DnRole = roles[roleid];
|
||||||
|
@ -52,10 +52,10 @@
|
|||||||
</td>
|
</td>
|
||||||
% endfor
|
% endfor
|
||||||
<td>
|
<td>
|
||||||
<a href="/modify?user=${user}" class="btn btn-xs blue pad" ><span class="glyphicon glyphicon-cog"></span> Modify</a>
|
<a href="/modify?user=${user | n,u}" class="btn btn-xs blue pad" ><span class="glyphicon glyphicon-cog"></span> Modify</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="/delete?user=${user}" data-toggle='confirmation-delete' class="btn btn-xs red pad"><span class="glyphicon glyphicon-remove-sign"></span> Delete</a>
|
<a href="/delete?user=${user | n,u}" data-toggle='confirmation-delete' class="btn btn-xs red pad"><span class="glyphicon glyphicon-remove-sign"></span> Delete</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
% endfor
|
% endfor
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<div class="well well-sm">
|
<div class="well well-sm">
|
||||||
<form method='POST' action='/selfmodify' autocomplete="off" role="form" class="form-signin" id="form">
|
<form method='POST' action='/selfmodify' autocomplete="off" role="form" class="form-signin" id="form">
|
||||||
<legend>Modify your attributes:</legend>
|
<legend>Modify your attributes:</legend>
|
||||||
${form}
|
${form | n}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
|
16
run_test.sh
16
run_test.sh
@ -5,11 +5,11 @@ Gre='\33[0;32m';
|
|||||||
RCol='\33[0m';
|
RCol='\33[0m';
|
||||||
|
|
||||||
cd `dirname $0`
|
cd `dirname $0`
|
||||||
python setup.py test &&\
|
python3 setup.py test #&&\
|
||||||
printf "\nPEP 8 compliance check:\n\n"
|
#printf "\nPEP 8 compliance check:\n\n"
|
||||||
pep8 \
|
#pep8 \
|
||||||
--repeat \
|
# --recurssive ./ \
|
||||||
--show-source \
|
# --show-source \
|
||||||
--exclude=.venv,.tox,dist,docs,build,*.egg,tests,misc . && \
|
# --exclude=.venv,.tox,dist,docs,build,*.egg,tests,misc . && \
|
||||||
printf "[${Gre}Passed${RCol}] Yeah! everything is clean\n\n" || \
|
# printf "[${Gre}Passed${RCol}] Yeah! everything is clean\n\n" || \
|
||||||
printf "[${Red}Failed${RCol}] Oh No! there is some mess to fix\n\n"
|
# printf "[${Red}Failed${RCol}] Oh No! there is some mess to fix\n\n"
|
||||||
|
26
setup.py
26
setup.py
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# vim:set expandtab tabstop=4 shiftwidth=4:
|
# vim:set expandtab tabstop=4 shiftwidth=4:
|
||||||
|
|
||||||
@ -17,17 +17,24 @@ data_dir = os.path.join(datarootdir, 'ldapcherry')
|
|||||||
config_dir = os.path.join(sysconfdir, 'ldapcherry')
|
config_dir = os.path.join(sysconfdir, 'ldapcherry')
|
||||||
small_description = 'A simple web application to manage Ldap entries'
|
small_description = 'A simple web application to manage Ldap entries'
|
||||||
|
|
||||||
|
sys.path.append('ldapcherry/')
|
||||||
|
from version import version
|
||||||
|
|
||||||
# change requirements according to python version
|
# change requirements according to python version
|
||||||
if sys.version_info[0] == 2:
|
if sys.version_info[0] == 2:
|
||||||
|
install_requires = [
|
||||||
|
'CherryPy >= 3.0.0,< 18.0.0',
|
||||||
|
'python-ldap',
|
||||||
|
'PyYAML',
|
||||||
|
'Mako'
|
||||||
|
],
|
||||||
|
elif sys.version_info[0] == 3:
|
||||||
install_requires = [
|
install_requires = [
|
||||||
'CherryPy >= 3.0.0',
|
'CherryPy >= 3.0.0',
|
||||||
'python-ldap',
|
'python-ldap',
|
||||||
'PyYAML',
|
'PyYAML',
|
||||||
'Mako'
|
'Mako'
|
||||||
],
|
],
|
||||||
elif sys.version_info[0] == 3:
|
|
||||||
print('unsupported version')
|
|
||||||
exit(1)
|
|
||||||
else:
|
else:
|
||||||
print('unsupported version')
|
print('unsupported version')
|
||||||
exit(1)
|
exit(1)
|
||||||
@ -90,7 +97,6 @@ resources_files = get_list_files(
|
|||||||
data_dir,
|
data_dir,
|
||||||
)
|
)
|
||||||
|
|
||||||
as_option_root
|
|
||||||
# add the configuration files if they don't exist
|
# add the configuration files if they don't exist
|
||||||
if as_option_root() or not os.path.exists(
|
if as_option_root() or not os.path.exists(
|
||||||
config_dir):
|
config_dir):
|
||||||
@ -109,7 +115,7 @@ if as_option_root() or not os.path.exists(
|
|||||||
setup(
|
setup(
|
||||||
name='ldapcherry',
|
name='ldapcherry',
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
version='0.3.5',
|
version=version,
|
||||||
author='Pierre-Francois Carpentier',
|
author='Pierre-Francois Carpentier',
|
||||||
author_email='carpentier.pf@gmail.com',
|
author_email='carpentier.pf@gmail.com',
|
||||||
packages=[
|
packages=[
|
||||||
@ -118,16 +124,18 @@ setup(
|
|||||||
'ldapcherry.ppolicy'
|
'ldapcherry.ppolicy'
|
||||||
],
|
],
|
||||||
data_files=resources_files,
|
data_files=resources_files,
|
||||||
scripts=['scripts/ldapcherryd'],
|
entry_points = {
|
||||||
|
'console_scripts': ['ldapcherryd = ldapcherry.cli:main']
|
||||||
|
},
|
||||||
url='https://github.com/kakwa/ldapcherry',
|
url='https://github.com/kakwa/ldapcherry',
|
||||||
license=license,
|
license=license,
|
||||||
description=small_description,
|
description=small_description,
|
||||||
long_description=description,
|
long_description=description,
|
||||||
install_requires=install_requires,
|
install_requires=install_requires,
|
||||||
tests_require=['pytest', 'pep8'],
|
tests_require=['pytest', 'pep8', 'pytidylib'],
|
||||||
cmdclass={'test': PyTest},
|
cmdclass={'test': PyTest},
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Development Status :: 3 - Alpha',
|
'Development Status :: 5 - Production/Stable',
|
||||||
'Environment :: Web Environment',
|
'Environment :: Web Environment',
|
||||||
'Framework :: CherryPy',
|
'Framework :: CherryPy',
|
||||||
'Intended Audience :: System Administrators',
|
'Intended Audience :: System Administrators',
|
||||||
|
@ -81,7 +81,7 @@ gidNumber:
|
|||||||
default: 10000
|
default: 10000
|
||||||
backends:
|
backends:
|
||||||
ldap: gidNumber
|
ldap: gidNumber
|
||||||
ad: GIDNumber
|
ad: gidNumber
|
||||||
shell:
|
shell:
|
||||||
description: "Shell of the user"
|
description: "Shell of the user"
|
||||||
display_name: "Shell"
|
display_name: "Shell"
|
||||||
|
136
tests/cfg/attributes_adldap.yml
Normal file
136
tests/cfg/attributes_adldap.yml
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
cn:
|
||||||
|
description: "First Name and Display Name"
|
||||||
|
display_name: "Display Name"
|
||||||
|
type: string
|
||||||
|
weight: 30
|
||||||
|
autofill:
|
||||||
|
function: lcDisplayName
|
||||||
|
args:
|
||||||
|
- $first-name
|
||||||
|
- $name
|
||||||
|
backends:
|
||||||
|
ldap: cn
|
||||||
|
ad: cn
|
||||||
|
first-name:
|
||||||
|
description: "First name of the user"
|
||||||
|
display_name: "First Name"
|
||||||
|
search_displayed: True
|
||||||
|
type: string
|
||||||
|
weight: 20
|
||||||
|
backends:
|
||||||
|
ldap: givenName
|
||||||
|
ad: givenName
|
||||||
|
name:
|
||||||
|
description: "Family name of the user"
|
||||||
|
display_name: "Name"
|
||||||
|
search_displayed: True
|
||||||
|
weight: 10
|
||||||
|
type: string
|
||||||
|
backends:
|
||||||
|
ldap: sn
|
||||||
|
ad: sn
|
||||||
|
email:
|
||||||
|
description: "Email of the user"
|
||||||
|
display_name: "Email"
|
||||||
|
search_displayed: True
|
||||||
|
type: email
|
||||||
|
weight: 40
|
||||||
|
autofill:
|
||||||
|
function: lcMail
|
||||||
|
args:
|
||||||
|
- $first-name
|
||||||
|
- $name
|
||||||
|
- '@example.com'
|
||||||
|
backends:
|
||||||
|
ldap: mail
|
||||||
|
ad: mail
|
||||||
|
uid:
|
||||||
|
description: "UID of the user"
|
||||||
|
display_name: "UID"
|
||||||
|
search_displayed: True
|
||||||
|
key: True
|
||||||
|
type: string
|
||||||
|
weight: 50
|
||||||
|
autofill:
|
||||||
|
function: lcUid
|
||||||
|
args:
|
||||||
|
- $first-name
|
||||||
|
- $name
|
||||||
|
backends:
|
||||||
|
ldap: uid
|
||||||
|
ad: sAMAccountName
|
||||||
|
uidNumber:
|
||||||
|
description: "User ID Number of the user"
|
||||||
|
display_name: "UID Number"
|
||||||
|
weight: 60
|
||||||
|
type: int
|
||||||
|
autofill:
|
||||||
|
function: lcUidNumber
|
||||||
|
args:
|
||||||
|
- $first-name
|
||||||
|
- $name
|
||||||
|
- '10000'
|
||||||
|
- '30000'
|
||||||
|
backends:
|
||||||
|
ldap: uidNumber
|
||||||
|
ad: uidNumber
|
||||||
|
gidNumber:
|
||||||
|
description: "Group ID Number of the user"
|
||||||
|
display_name: "GID Number"
|
||||||
|
weight: 70
|
||||||
|
type: int
|
||||||
|
autofill:
|
||||||
|
function: lcUidNumber
|
||||||
|
args:
|
||||||
|
- $first-name
|
||||||
|
- $name
|
||||||
|
- '10000'
|
||||||
|
- '30000'
|
||||||
|
backends:
|
||||||
|
ldap: gidNumber
|
||||||
|
ad: gidNumber
|
||||||
|
shell:
|
||||||
|
description: "Shell of the user"
|
||||||
|
display_name: "Shell"
|
||||||
|
weight: 80
|
||||||
|
self: True
|
||||||
|
type: stringlist
|
||||||
|
values:
|
||||||
|
- /bin/bash
|
||||||
|
- /bin/zsh
|
||||||
|
- /bin/sh
|
||||||
|
backends:
|
||||||
|
ldap: loginShell
|
||||||
|
ad: loginShell
|
||||||
|
home:
|
||||||
|
description: "Home user path"
|
||||||
|
display_name: "Home"
|
||||||
|
weight: 90
|
||||||
|
type: string
|
||||||
|
autofill:
|
||||||
|
function: lcHomeDir
|
||||||
|
args:
|
||||||
|
- $first-name
|
||||||
|
- $name
|
||||||
|
- /home/
|
||||||
|
backends:
|
||||||
|
ldap: homeDirectory
|
||||||
|
ad: homeDirectory
|
||||||
|
password:
|
||||||
|
description: "Password of the user"
|
||||||
|
display_name: "Password"
|
||||||
|
weight: 31
|
||||||
|
self: True
|
||||||
|
type: password
|
||||||
|
backends:
|
||||||
|
ldap: userPassword
|
||||||
|
ad: unicodePwd
|
||||||
|
|
||||||
|
logscript:
|
||||||
|
description: "Windows login script"
|
||||||
|
display_name: "Login script"
|
||||||
|
weight: 100
|
||||||
|
type: fix
|
||||||
|
value: login1.bat
|
||||||
|
backends:
|
||||||
|
ad: scriptPath
|
186
tests/cfg/ldapcherry_adldap.cfg
Normal file
186
tests/cfg/ldapcherry_adldap.cfg
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
# global parameters
|
||||||
|
[global]
|
||||||
|
|
||||||
|
# listing interface
|
||||||
|
server.socket_host = '127.0.0.1'
|
||||||
|
# port
|
||||||
|
server.socket_port = 8080
|
||||||
|
# number of threads
|
||||||
|
server.thread_pool = 8
|
||||||
|
#don't show traceback on error
|
||||||
|
request.show_tracebacks = False
|
||||||
|
|
||||||
|
# log configuration
|
||||||
|
# /!\ you can't have multiple log handlers
|
||||||
|
#####################################
|
||||||
|
# configuration to log in files #
|
||||||
|
#####################################
|
||||||
|
## logger 'file' for access log
|
||||||
|
#log.access_handler = 'file'
|
||||||
|
## logger syslog for error and ldapcherry log
|
||||||
|
#log.error_handler = 'file'
|
||||||
|
## access log file
|
||||||
|
#log.access_file = '/tmp/ldapcherry_access.log'
|
||||||
|
## error and ldapcherry log file
|
||||||
|
#log.error_file = '/tmp/ldapcherry_error.log'
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
# configuration to log in syslog #
|
||||||
|
#####################################
|
||||||
|
# logger syslog for access log
|
||||||
|
#log.access_handler = 'syslog'
|
||||||
|
## logger syslog for error and ldapcherry log
|
||||||
|
log.error_handler = 'syslog'
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
# configuration to not log at all #
|
||||||
|
#####################################
|
||||||
|
# logger none for access log
|
||||||
|
log.access_handler = 'none'
|
||||||
|
# logger none for error and ldapcherry log
|
||||||
|
#log.error_handler = 'none'
|
||||||
|
|
||||||
|
# log level
|
||||||
|
log.level = 'info'
|
||||||
|
|
||||||
|
# session configuration
|
||||||
|
# activate session
|
||||||
|
tools.sessions.on = True
|
||||||
|
# session timeout
|
||||||
|
tools.sessions.timeout = 7200
|
||||||
|
# file session storage(to use if multiple processes,
|
||||||
|
# default is in RAM and per process)
|
||||||
|
#tools.sessions.storage_type = "file"
|
||||||
|
# session
|
||||||
|
#tools.sessions.storage_path = "/var/lib/ldapcherry/sessions"
|
||||||
|
|
||||||
|
[attributes]
|
||||||
|
|
||||||
|
# file discribing form content
|
||||||
|
attributes.file = './tests/cfg/attributes_adldap.yml'
|
||||||
|
|
||||||
|
[roles]
|
||||||
|
|
||||||
|
# file listing roles
|
||||||
|
roles.file = './tests/cfg/roles_adldap.yml'
|
||||||
|
|
||||||
|
[search]
|
||||||
|
|
||||||
|
# minimum lenght for search forms
|
||||||
|
min.lenght = 0
|
||||||
|
|
||||||
|
[backends]
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
# configuration of ldap backend #
|
||||||
|
#####################################
|
||||||
|
|
||||||
|
# name of the module
|
||||||
|
ldap.module = 'ldapcherry.backend.backendLdap'
|
||||||
|
# display name of the ldap
|
||||||
|
ldap.display_name = 'My Ldap Directory'
|
||||||
|
|
||||||
|
# uri of the ldap directory
|
||||||
|
ldap.uri = 'ldap://ldap.ldapcherry.org:390'
|
||||||
|
# ca to use for ssl/tls connexion
|
||||||
|
#ldap.ca = '/etc/dnscherry/TEST-cacert.pem'
|
||||||
|
# use start tls
|
||||||
|
#ldap.starttls = 'off'
|
||||||
|
# check server certificate (for tls)
|
||||||
|
#ldap.checkcert = 'off'
|
||||||
|
# bind dn to the ldap
|
||||||
|
ldap.binddn = 'cn=dnscherry,dc=example,dc=org'
|
||||||
|
# password of the bind dn
|
||||||
|
ldap.password = 'password'
|
||||||
|
# timeout of ldap connexion (in second)
|
||||||
|
ldap.timeout = 1
|
||||||
|
|
||||||
|
# groups dn
|
||||||
|
ldap.groupdn = 'ou=group,dc=example,dc=org'
|
||||||
|
# users dn
|
||||||
|
ldap.userdn = 'ou=people,dc=example,dc=org'
|
||||||
|
# ldapsearch filter to get a user
|
||||||
|
ldap.user_filter_tmpl = '(uid=%(username)s)'
|
||||||
|
# ldapsearch filter to get groups of a user
|
||||||
|
ldap.group_filter_tmpl = '(member=uid=%(username)s,ou=People,dc=example,dc=org)'
|
||||||
|
# filter to search users
|
||||||
|
ldap.search_filter_tmpl = '(|(uid=%(searchstring)s*)(sn=%(searchstring)s*))'
|
||||||
|
|
||||||
|
# ldap group attributes and how to fill them
|
||||||
|
ldap.group_attr.member = "%(dn)s"
|
||||||
|
#ldap.group_attr.memberUid = "%(uid)s"
|
||||||
|
# object classes of a user entry
|
||||||
|
ldap.objectclasses = 'top, person, posixAccount, inetOrgPerson'
|
||||||
|
# dn entry attribute for an ldap user
|
||||||
|
ldap.dn_user_attr = 'uid'
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
# configuration of ad backend #
|
||||||
|
#####################################
|
||||||
|
|
||||||
|
## Name of the backend
|
||||||
|
ad.module = 'ldapcherry.backend.backendAD'
|
||||||
|
# display name of the ldap
|
||||||
|
ad.display_name = 'My Active Directory'
|
||||||
|
# ad domain
|
||||||
|
ad.domain = 'DC.LDAPCHERRY.ORG'
|
||||||
|
# ad login
|
||||||
|
ad.login = 'administrator'
|
||||||
|
# ad password
|
||||||
|
ad.password = 'qwertyP455'
|
||||||
|
# ad uri
|
||||||
|
ad.uri = 'ldaps://ad.ldapcherry.org'
|
||||||
|
|
||||||
|
# ca to use for ssl/tls connexion
|
||||||
|
#ad.ca = '/etc/dnscherry/TEST-cacert.pem'
|
||||||
|
# use start tls
|
||||||
|
ad.starttls = 'off'
|
||||||
|
# check server certificate (for tls)
|
||||||
|
ad.checkcert = 'off'
|
||||||
|
|
||||||
|
[ppolicy]
|
||||||
|
|
||||||
|
# password policy module
|
||||||
|
ppolicy.module = 'ldapcherry.ppolicy.simple'
|
||||||
|
|
||||||
|
# parameters of the module
|
||||||
|
min_length = 8
|
||||||
|
min_upper = 1
|
||||||
|
min_digit = 1
|
||||||
|
|
||||||
|
# authentification parameters
|
||||||
|
[auth]
|
||||||
|
|
||||||
|
# Auth mode
|
||||||
|
# * and: user must authenticate on all backends
|
||||||
|
# * or: user must authenticate on one of the backend
|
||||||
|
# * none: disable authentification
|
||||||
|
# * custom: custom authentification module (need auth.module param)
|
||||||
|
auth.mode = 'or'
|
||||||
|
|
||||||
|
# custom auth module to load
|
||||||
|
#auth.module = 'ldapcherry.auth.modNone'
|
||||||
|
|
||||||
|
# resources parameters
|
||||||
|
[resources]
|
||||||
|
# templates directory
|
||||||
|
templates.dir = './resources/templates/'
|
||||||
|
|
||||||
|
[/static]
|
||||||
|
# enable serving static file through ldapcherry
|
||||||
|
# set to False if files served directly by an
|
||||||
|
# http server for better performance
|
||||||
|
tools.staticdir.on = True
|
||||||
|
# static resources directory (js, css, images...)
|
||||||
|
tools.staticdir.dir = './resources/static/'
|
||||||
|
|
||||||
|
## custom javascript files
|
||||||
|
#[/custom]
|
||||||
|
#
|
||||||
|
## enable serving static file through ldapcherry
|
||||||
|
## set to False if files served directly by an
|
||||||
|
## http server for better performance
|
||||||
|
#tools.staticdir.on = True
|
||||||
|
|
||||||
|
## path to directory containing js files
|
||||||
|
## use it to add custom auto-fill functions
|
@ -21,10 +21,10 @@ users:
|
|||||||
display_name: Administrators Level 3
|
display_name: Administrators Level 3
|
||||||
description: description
|
description: description
|
||||||
subroles: {}
|
subroles: {}
|
||||||
developpers:
|
developers:
|
||||||
backends_groups:
|
backends_groups:
|
||||||
ad: [Domain Users]
|
ad: [Domain Users]
|
||||||
ldap: ['cn=developpers,ou=group,dc=example,dc=com']
|
ldap: ['cn=developers,ou=group,dc=example,dc=com']
|
||||||
display_name: Developpers
|
display_name: Developpers
|
||||||
description: description
|
description: description
|
||||||
subroles: {}
|
subroles: {}
|
||||||
|
@ -23,12 +23,12 @@ admin-lv2:
|
|||||||
ad:
|
ad:
|
||||||
- Domain Users
|
- Domain Users
|
||||||
|
|
||||||
developpers:
|
developers:
|
||||||
display_name: Developpers
|
display_name: Developpers
|
||||||
description: description
|
description: description
|
||||||
backends_groups:
|
backends_groups:
|
||||||
ldap:
|
ldap:
|
||||||
- cn=developpers,ou=group,dc=example,dc=com
|
- cn=developers,ou=group,dc=example,dc=com
|
||||||
- cn=users,ou=group,dc=example,dc=com
|
- cn=users,ou=group,dc=example,dc=com
|
||||||
ad:
|
ad:
|
||||||
- Domain Users
|
- Domain Users
|
||||||
|
41
tests/cfg/roles_adldap.yml
Normal file
41
tests/cfg/roles_adldap.yml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
admin-lv3:
|
||||||
|
display_name: Administrators Level 3
|
||||||
|
description: Super administrators of the system
|
||||||
|
backends_groups:
|
||||||
|
# ldap:
|
||||||
|
# - cn=dns admins,ou=Group,dc=example,dc=org
|
||||||
|
# - cn=nagios admins,ou=Group,dc=example,dc=org
|
||||||
|
# - cn=puppet admins,ou=Group,dc=example,dc=org
|
||||||
|
# - cn=users,ou=Group,dc=example,dc=org
|
||||||
|
ad:
|
||||||
|
- Administrators
|
||||||
|
- Domain Controllers
|
||||||
|
- Group Policy Creator Owners
|
||||||
|
|
||||||
|
admin-lv2:
|
||||||
|
display_name: Administrators Level 2
|
||||||
|
description: Basic administrators of the system
|
||||||
|
LC_admins: True
|
||||||
|
backends_groups:
|
||||||
|
# ldap:
|
||||||
|
# - cn=nagios admins,ou=Group,dc=example,dc=org
|
||||||
|
# - cn=users,ou=Group,dc=example,dc=org
|
||||||
|
ad:
|
||||||
|
- Administrators
|
||||||
|
|
||||||
|
#developers:
|
||||||
|
# display_name: Developpers
|
||||||
|
# description: Developpers of the system
|
||||||
|
# backends_groups:
|
||||||
|
# ldap:
|
||||||
|
# - cn=developers,ou=Group,dc=example,dc=org
|
||||||
|
# - cn=users,ou=Group,dc=example,dc=org
|
||||||
|
|
||||||
|
#users:
|
||||||
|
# display_name: Simple Users
|
||||||
|
# description: Basic users of the system
|
||||||
|
# backends_groups:
|
||||||
|
## ldap:
|
||||||
|
## - cn=users,ou=Group,dc=example,dc=org
|
||||||
|
# ad:
|
||||||
|
# - Domain Users
|
@ -23,12 +23,12 @@ admin -lv2:
|
|||||||
ad:
|
ad:
|
||||||
- Domain Users
|
- Domain Users
|
||||||
|
|
||||||
developpers:
|
developers:
|
||||||
display_name: Developpers
|
display_name: Developpers
|
||||||
description: description
|
description: description
|
||||||
backends_groups:
|
backends_groups:
|
||||||
ldap:
|
ldap:
|
||||||
- cn=developpers,ou=group,dc=example,dc=com
|
- cn=developers,ou=group,dc=example,dc=com
|
||||||
- cn=users,ou=group,dc=example,dc=com
|
- cn=users,ou=group,dc=example,dc=com
|
||||||
ad:
|
ad:
|
||||||
- Domain Users
|
- Domain Users
|
||||||
|
@ -23,12 +23,12 @@ admin -lv3:
|
|||||||
ad:
|
ad:
|
||||||
- Domain Users
|
- Domain Users
|
||||||
|
|
||||||
developpers:
|
developers:
|
||||||
display_name: Developpers
|
display_name: Developpers
|
||||||
description: description
|
description: description
|
||||||
backends_groups:
|
backends_groups:
|
||||||
ldap:
|
ldap:
|
||||||
- cn=developpers,ou=group,dc=example,dc=com
|
- cn=developers,ou=group,dc=example,dc=com
|
||||||
- cn=users,ou=group,dc=example,dc=com
|
- cn=users,ou=group,dc=example,dc=com
|
||||||
ad:
|
ad:
|
||||||
- Domain Users
|
- Domain Users
|
||||||
|
@ -17,12 +17,12 @@ admin-lv2:
|
|||||||
display_name: Administrators Level 2
|
display_name: Administrators Level 2
|
||||||
description: description
|
description: description
|
||||||
|
|
||||||
developpers:
|
developers:
|
||||||
display_name: Developpers
|
display_name: Developpers
|
||||||
description: description
|
description: description
|
||||||
backends_groups:
|
backends_groups:
|
||||||
ldap:
|
ldap:
|
||||||
- cn=developpers,ou=group,dc=example,dc=com
|
- cn=developers,ou=group,dc=example,dc=com
|
||||||
- cn=users,ou=group,dc=example,dc=com
|
- cn=users,ou=group,dc=example,dc=com
|
||||||
ad:
|
ad:
|
||||||
- Domain Users
|
- Domain Users
|
||||||
|
@ -22,12 +22,12 @@ admin-lv2:
|
|||||||
ad:
|
ad:
|
||||||
- Domain Users
|
- Domain Users
|
||||||
|
|
||||||
developpers:
|
developers:
|
||||||
display_name: Developpers
|
display_name: Developpers
|
||||||
description: description
|
description: description
|
||||||
backends_groups:
|
backends_groups:
|
||||||
ldap:
|
ldap:
|
||||||
- cn=developpers,ou=group,dc=example,dc=com
|
- cn=developers,ou=group,dc=example,dc=com
|
||||||
- cn=users,ou=group,dc=example,dc=com
|
- cn=users,ou=group,dc=example,dc=com
|
||||||
ad:
|
ad:
|
||||||
- Domain Users
|
- Domain Users
|
||||||
|
@ -17,12 +17,12 @@ admin-lv2:
|
|||||||
- cn=nagios admins,ou=Group,dc=example,dc=org
|
- cn=nagios admins,ou=Group,dc=example,dc=org
|
||||||
- cn=users,ou=Group,dc=example,dc=org
|
- cn=users,ou=Group,dc=example,dc=org
|
||||||
|
|
||||||
developpers:
|
developers:
|
||||||
display_name: Developpers
|
display_name: Developpers
|
||||||
description: Developpers of the system
|
description: Developpers of the system
|
||||||
backends_groups:
|
backends_groups:
|
||||||
ldap:
|
ldap:
|
||||||
- cn=developpers,ou=Group,dc=example,dc=org
|
- cn=developers,ou=Group,dc=example,dc=org
|
||||||
- cn=users,ou=Group,dc=example,dc=org
|
- cn=users,ou=Group,dc=example,dc=org
|
||||||
|
|
||||||
users:
|
users:
|
||||||
|
@ -1,8 +1,16 @@
|
|||||||
import os
|
import os
|
||||||
def travis_disabled(f):
|
def travis_disabled(f):
|
||||||
def _decorator(f):
|
def _decorator(f):
|
||||||
print 'test has been disabled on travis'
|
print('test has been disabled on travis')
|
||||||
if 'TRAVIS' in os.environ and os.environ['TRAVIS'] == 'yes':
|
if 'TRAVIS' in os.environ and os.environ['TRAVIS'] == 'yes':
|
||||||
return _decorator
|
return _decorator
|
||||||
else:
|
else:
|
||||||
return f
|
return f
|
||||||
|
|
||||||
|
def slow_disabled(f):
|
||||||
|
def _decorator(f):
|
||||||
|
print('test has been disabled by env var LCNOSLOW')
|
||||||
|
if 'LCNOSLOW' in os.environ and os.environ['LCNOSLOW'] == 'yes':
|
||||||
|
return _decorator
|
||||||
|
else:
|
||||||
|
return f
|
||||||
|
@ -1,243 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
# Copyright (c) 2007-2008 Mozilla Foundation
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
# copy of this software and associated documentation files (the "Software"),
|
|
||||||
# to deal in the Software without restriction, including without limitation
|
|
||||||
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
# and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
# Software is furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in
|
|
||||||
# all copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
# DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
from __future__ import print_function, with_statement
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import re
|
|
||||||
import string
|
|
||||||
import gzip
|
|
||||||
|
|
||||||
# Several "try" blocks for python2/3 differences (@secretrobotron)
|
|
||||||
try:
|
|
||||||
import httplib
|
|
||||||
except ImportError:
|
|
||||||
import http.client as httplib
|
|
||||||
|
|
||||||
try:
|
|
||||||
import urlparse
|
|
||||||
except ImportError:
|
|
||||||
import urllib.parse as urlparse
|
|
||||||
|
|
||||||
try:
|
|
||||||
from BytesIO import BytesIO
|
|
||||||
except ImportError:
|
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
try:
|
|
||||||
maketrans = str.maketrans
|
|
||||||
except AttributeError:
|
|
||||||
maketrans = string.maketrans
|
|
||||||
|
|
||||||
#
|
|
||||||
# Begin
|
|
||||||
#
|
|
||||||
extPat = re.compile(r'^.*\.([A-Za-z]+)$')
|
|
||||||
extDict = {
|
|
||||||
'html' : 'text/html',
|
|
||||||
'htm' : 'text/html',
|
|
||||||
'xhtml' : 'application/xhtml+xml',
|
|
||||||
'xht' : 'application/xhtml+xml',
|
|
||||||
'xml' : 'application/xml',
|
|
||||||
}
|
|
||||||
|
|
||||||
forceXml = False
|
|
||||||
forceHtml = False
|
|
||||||
gnu = False
|
|
||||||
errorsOnly = False
|
|
||||||
encoding = None
|
|
||||||
fileName = None
|
|
||||||
contentType = None
|
|
||||||
inputHandle = None
|
|
||||||
service = 'https://html5.validator.nu/'
|
|
||||||
|
|
||||||
argv = sys.argv[1:]
|
|
||||||
|
|
||||||
#
|
|
||||||
# Parse command line input
|
|
||||||
#
|
|
||||||
for arg in argv:
|
|
||||||
if '--help' == arg:
|
|
||||||
print('-h : force text/html')
|
|
||||||
print('-x : force application/xhtml+xml')
|
|
||||||
print('-g : GNU output')
|
|
||||||
print('-e : errors only (no info or warnings)')
|
|
||||||
print('--encoding=foo : declare encoding foo')
|
|
||||||
print('--service=url : the address of the HTML5 validator')
|
|
||||||
print('One file argument allowed. Leave out to read from stdin.')
|
|
||||||
sys.exit(0)
|
|
||||||
elif arg.startswith('--encoding='):
|
|
||||||
encoding = arg[11:]
|
|
||||||
elif arg.startswith('--service='):
|
|
||||||
service = arg[10:]
|
|
||||||
elif arg.startswith('--'):
|
|
||||||
sys.stderr.write('Unknown argument %s.\n' % arg)
|
|
||||||
sys.exit(2)
|
|
||||||
elif arg.startswith('-'):
|
|
||||||
for c in arg[1:]:
|
|
||||||
if 'x' == c:
|
|
||||||
forceXml = True
|
|
||||||
elif 'h' == c:
|
|
||||||
forceHtml = True
|
|
||||||
elif 'g' == c:
|
|
||||||
gnu = True
|
|
||||||
elif 'e' == c:
|
|
||||||
errorsOnly = True
|
|
||||||
else:
|
|
||||||
sys.stderr.write('Unknown argument %s.\n' % arg)
|
|
||||||
sys.exit(3)
|
|
||||||
else:
|
|
||||||
if fileName:
|
|
||||||
sys.stderr.write('Cannot have more than one input file.\n')
|
|
||||||
sys.exit(1)
|
|
||||||
fileName = arg
|
|
||||||
|
|
||||||
#
|
|
||||||
# Ensure a maximum of one forced output type
|
|
||||||
#
|
|
||||||
if forceXml and forceHtml:
|
|
||||||
sys.stderr.write('Cannot force HTML and XHTML at the same time.\n')
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
#
|
|
||||||
# Set contentType
|
|
||||||
#
|
|
||||||
if forceXml:
|
|
||||||
contentType = 'application/xhtml+xml'
|
|
||||||
elif forceHtml:
|
|
||||||
contentType = 'text/html'
|
|
||||||
elif fileName:
|
|
||||||
m = extPat.match(fileName)
|
|
||||||
if m:
|
|
||||||
ext = m.group(1)
|
|
||||||
ext = ext.translate(maketrans(string.ascii_uppercase, string.ascii_lowercase))
|
|
||||||
if ext in extDict:
|
|
||||||
contentType = extDict[ext]
|
|
||||||
else:
|
|
||||||
sys.stderr.write('Unable to guess Content-Type from file name. Please force the type.\n')
|
|
||||||
sys.exit(3)
|
|
||||||
else:
|
|
||||||
sys.stderr.write('Could not extract a filename extension. Please force the type.\n')
|
|
||||||
sys.exit(6)
|
|
||||||
else:
|
|
||||||
sys.stderr.write('Need to force HTML or XHTML when reading from stdin.\n')
|
|
||||||
sys.exit(4)
|
|
||||||
|
|
||||||
if encoding:
|
|
||||||
contentType = '%s; charset=%s' % (contentType, encoding)
|
|
||||||
|
|
||||||
#
|
|
||||||
# Read the file argument (or STDIN)
|
|
||||||
#
|
|
||||||
if fileName:
|
|
||||||
inputHandle = fileName
|
|
||||||
else:
|
|
||||||
inputHandle = sys.stdin
|
|
||||||
|
|
||||||
with open(inputHandle, mode='rb') as inFile:
|
|
||||||
data = inFile.read()
|
|
||||||
with BytesIO() as buf:
|
|
||||||
# we could use another with block here, but it requires Python 2.7+
|
|
||||||
zipFile = gzip.GzipFile(fileobj=buf, mode='wb')
|
|
||||||
zipFile.write(data)
|
|
||||||
zipFile.close()
|
|
||||||
gzippeddata = buf.getvalue()
|
|
||||||
|
|
||||||
#
|
|
||||||
# Prepare the request
|
|
||||||
#
|
|
||||||
url = service
|
|
||||||
|
|
||||||
if gnu:
|
|
||||||
url = url + '?out=gnu'
|
|
||||||
else:
|
|
||||||
url = url + '?out=text'
|
|
||||||
|
|
||||||
if errorsOnly:
|
|
||||||
url = url + '&level=error'
|
|
||||||
|
|
||||||
connection = None
|
|
||||||
response = None
|
|
||||||
status = 302
|
|
||||||
redirectCount = 0
|
|
||||||
|
|
||||||
#
|
|
||||||
# Make the request
|
|
||||||
#
|
|
||||||
while status in (302,301,307) and redirectCount < 10:
|
|
||||||
if redirectCount > 0:
|
|
||||||
url = response.getheader('Location')
|
|
||||||
parsed = urlparse.urlsplit(url)
|
|
||||||
|
|
||||||
if redirectCount > 0:
|
|
||||||
connection.close() # previous connection
|
|
||||||
print('Redirecting to %s' % url)
|
|
||||||
print('Please press enter to continue or type \'stop\' followed by enter to stop.')
|
|
||||||
if raw_input() != '':
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
if parsed.scheme == 'https':
|
|
||||||
connection = httplib.HTTPSConnection(parsed[1])
|
|
||||||
else:
|
|
||||||
connection = httplib.HTTPConnection(parsed[1])
|
|
||||||
|
|
||||||
headers = {
|
|
||||||
'Accept-Encoding': 'gzip',
|
|
||||||
'Content-Type': contentType,
|
|
||||||
'Content-Encoding': 'gzip',
|
|
||||||
'Content-Length': len(gzippeddata),
|
|
||||||
}
|
|
||||||
urlSuffix = '%s?%s' % (parsed[2], parsed[3])
|
|
||||||
|
|
||||||
connection.connect()
|
|
||||||
connection.request('POST', urlSuffix, body=gzippeddata, headers=headers)
|
|
||||||
|
|
||||||
response = connection.getresponse()
|
|
||||||
status = response.status
|
|
||||||
|
|
||||||
redirectCount += 1
|
|
||||||
|
|
||||||
#
|
|
||||||
# Handle the response
|
|
||||||
#
|
|
||||||
if status != 200:
|
|
||||||
sys.stderr.write('%s %s\n' % (status, response.reason))
|
|
||||||
sys.exit(5)
|
|
||||||
|
|
||||||
if response.getheader('Content-Encoding', 'identity').lower() == 'gzip':
|
|
||||||
response = gzip.GzipFile(fileobj=BytesIO(response.read()))
|
|
||||||
|
|
||||||
if fileName and gnu:
|
|
||||||
quotedName = '"%s"' % fileName.replace("'", '\\042')
|
|
||||||
for line in response.read().split('\n'):
|
|
||||||
if line:
|
|
||||||
sys.stdout.write(quotedName)
|
|
||||||
sys.stdout.write(line + '\n')
|
|
||||||
else:
|
|
||||||
output = response.read()
|
|
||||||
# python2/3 difference in output's type
|
|
||||||
if not isinstance(output, str):
|
|
||||||
output = output.decode('utf-8')
|
|
||||||
sys.stdout.write(output)
|
|
||||||
|
|
||||||
connection.close()
|
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
@ -6,10 +6,12 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import sys
|
import sys
|
||||||
from sets import Set
|
|
||||||
from ldapcherry.attributes import Attributes
|
from ldapcherry.attributes import Attributes
|
||||||
from ldapcherry.exceptions import *
|
from ldapcherry.exceptions import *
|
||||||
from ldapcherry.pyyamlwrapper import DumplicatedKey, RelationError
|
from ldapcherry.pyyamlwrapper import DumplicatedKey, RelationError
|
||||||
|
if sys.version < '3':
|
||||||
|
from sets import Set as set
|
||||||
|
|
||||||
|
|
||||||
class TestError(object):
|
class TestError(object):
|
||||||
|
|
||||||
@ -27,7 +29,7 @@ class TestError(object):
|
|||||||
def testGetSelfAttributes(self):
|
def testGetSelfAttributes(self):
|
||||||
inv = Attributes('./tests/cfg/attributes.yml')
|
inv = Attributes('./tests/cfg/attributes.yml')
|
||||||
ret = inv.get_backends()
|
ret = inv.get_backends()
|
||||||
expected = Set(['ldap', 'ad'])
|
expected = set(['ldap', 'ad'])
|
||||||
assert ret == expected
|
assert ret == expected
|
||||||
|
|
||||||
def testGetSearchAttributes(self):
|
def testGetSearchAttributes(self):
|
||||||
@ -40,6 +42,7 @@ class TestError(object):
|
|||||||
inv = Attributes('./tests/cfg/attributes.yml')
|
inv = Attributes('./tests/cfg/attributes.yml')
|
||||||
ret = inv.get_backend_attributes('ldap')
|
ret = inv.get_backend_attributes('ldap')
|
||||||
expected = ['shell', 'cn', 'userPassword', 'uidNumber', 'gidNumber', 'sn', 'home', 'givenName', 'email', 'uid']
|
expected = ['shell', 'cn', 'userPassword', 'uidNumber', 'gidNumber', 'sn', 'home', 'givenName', 'email', 'uid']
|
||||||
|
expected.sort()
|
||||||
assert ret == expected
|
assert ret == expected
|
||||||
|
|
||||||
def testGetKey(self):
|
def testGetKey(self):
|
||||||
|
204
tests/test_BackendAD.py
Normal file
204
tests/test_BackendAD.py
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import with_statement
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import sys
|
||||||
|
from ldapcherry.backend.backendAD import Backend
|
||||||
|
from ldapcherry.exceptions import *
|
||||||
|
from disable import travis_disabled
|
||||||
|
import cherrypy
|
||||||
|
import logging
|
||||||
|
if sys.version < '3':
|
||||||
|
from sets import Set as set
|
||||||
|
|
||||||
|
|
||||||
|
cfg = {
|
||||||
|
'display_name': u'test☭',
|
||||||
|
'domain': 'DC.LDAPCHERRY.ORG',
|
||||||
|
'login': 'Administrator',
|
||||||
|
'password': 'qwertyP455',
|
||||||
|
'uri': 'ldaps://ad.ldapcherry.org',
|
||||||
|
'checkcert': 'off',
|
||||||
|
}
|
||||||
|
|
||||||
|
def syslog_error(msg='', context='',
|
||||||
|
severity=logging.INFO, traceback=False):
|
||||||
|
pass
|
||||||
|
|
||||||
|
cherrypy.log.error = syslog_error
|
||||||
|
attr = ['shell', 'cn', 'sAMAccountName', 'uidNumber', 'gidNumber', 'home', 'unicodePwd', 'givenName', 'email', 'sn']
|
||||||
|
|
||||||
|
default_user = {
|
||||||
|
'sAMAccountName': u'☭default_user',
|
||||||
|
'sn': u'test☭1',
|
||||||
|
'cn': u'test☭2',
|
||||||
|
'unicodePwd': u'test☭P666',
|
||||||
|
'uidNumber': '42',
|
||||||
|
'gidNumber': '42',
|
||||||
|
'homeDirectory': '/home/test/'
|
||||||
|
}
|
||||||
|
|
||||||
|
default_user2 = {
|
||||||
|
'sAMAccountName': u'☭default_user2',
|
||||||
|
'sn': u'test☭ 2',
|
||||||
|
'cn': u'test☭ 2',
|
||||||
|
'unicodePwd': u'test☭P666',
|
||||||
|
'uidNumber': '42',
|
||||||
|
'gidNumber': '42',
|
||||||
|
'homeDirectory': '/home/test/'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
default_groups = ['Domain Admins', 'Backup Operators']
|
||||||
|
|
||||||
|
|
||||||
|
class TestError(object):
|
||||||
|
|
||||||
|
@travis_disabled
|
||||||
|
def testNominal(self):
|
||||||
|
inv = Backend(cfg, cherrypy.log, u'test☭', attr, 'sAMAccountName')
|
||||||
|
return True
|
||||||
|
|
||||||
|
@travis_disabled
|
||||||
|
def testAuthSuccess(self):
|
||||||
|
inv = Backend(cfg, cherrypy.log, u'test☭', attr, 'sAMAccountName')
|
||||||
|
ret = inv.auth('Administrator', 'qwertyP455')
|
||||||
|
assert ret == True
|
||||||
|
|
||||||
|
@travis_disabled
|
||||||
|
def testAuthFailure(self):
|
||||||
|
inv = Backend(cfg, cherrypy.log, u'test☭', attr, 'sAMAccountName')
|
||||||
|
res = inv.auth('notauser', 'password') or inv.auth(u'☭default_user', 'notapassword')
|
||||||
|
assert res == False
|
||||||
|
|
||||||
|
@travis_disabled
|
||||||
|
def testsetPassword(self):
|
||||||
|
inv = Backend(cfg, cherrypy.log, u'test☭', attr, 'sAMAccountName')
|
||||||
|
try:
|
||||||
|
inv.add_user(default_user.copy())
|
||||||
|
inv.add_to_groups(u'☭default_user', default_groups)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
inv.set_attrs(u'☭default_user', {'unicodePwd': u'test☭P66642$'})
|
||||||
|
ret = inv.auth(u'☭default_user', u'test☭P66642$')
|
||||||
|
inv.del_user(u'☭default_user')
|
||||||
|
assert ret == True
|
||||||
|
|
||||||
|
@travis_disabled
|
||||||
|
def testGetUser(self):
|
||||||
|
inv = Backend(cfg, cherrypy.log, u'test☭', attr, 'sAMAccountName')
|
||||||
|
try:
|
||||||
|
inv.add_user(default_user.copy())
|
||||||
|
inv.add_to_groups(u'☭default_user', default_groups)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
ret = inv.get_user(u'☭default_user')
|
||||||
|
expected = default_user
|
||||||
|
inv.del_user(u'☭default_user')
|
||||||
|
for i in default_user:
|
||||||
|
if i != 'unicodePwd':
|
||||||
|
assert ret[i] == expected[i]
|
||||||
|
|
||||||
|
@travis_disabled
|
||||||
|
def testGetGroups(self):
|
||||||
|
inv = Backend(cfg, cherrypy.log, u'test☭', attr, 'sAMAccountName')
|
||||||
|
try:
|
||||||
|
inv.add_user(default_user.copy())
|
||||||
|
inv.add_to_groups(u'☭default_user', default_groups)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
ret = inv.get_groups(u'☭default_user')
|
||||||
|
expected = ['Domain Admins', 'Backup Operators']
|
||||||
|
inv.del_user(u'☭default_user')
|
||||||
|
assert ret == expected
|
||||||
|
|
||||||
|
@travis_disabled
|
||||||
|
def testSearchUser(self):
|
||||||
|
inv = Backend(cfg, cherrypy.log, u'test☭', attr, 'sAMAccountName')
|
||||||
|
try:
|
||||||
|
inv.del_user(u'☭default_user')
|
||||||
|
except: pass
|
||||||
|
try:
|
||||||
|
inv.del_user(u'☭default_user2')
|
||||||
|
except: pass
|
||||||
|
try:
|
||||||
|
inv.add_user(default_user.copy())
|
||||||
|
except: pass
|
||||||
|
inv.add_user(default_user2.copy())
|
||||||
|
ret = inv.search(u'test☭')
|
||||||
|
expected = [u'☭default_user', u'☭default_user2']
|
||||||
|
inv.del_user(u'☭default_user')
|
||||||
|
inv.del_user(u'☭default_user2')
|
||||||
|
assert set(ret.keys()) == set(expected)
|
||||||
|
|
||||||
|
@travis_disabled
|
||||||
|
def testAddUser(self):
|
||||||
|
try:
|
||||||
|
inv.del_user(u'test☭')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
inv = Backend(cfg, cherrypy.log, u'test☭', attr, 'sAMAccountName')
|
||||||
|
user = {
|
||||||
|
'sAMAccountName': u'test☭',
|
||||||
|
'sn': u'test☭',
|
||||||
|
'cn': u'test☭',
|
||||||
|
'unicodePwd': u'test☭0918M',
|
||||||
|
'uidNumber': '42',
|
||||||
|
'gidNumber': '42',
|
||||||
|
'homeDirectory': '/home/test/'
|
||||||
|
}
|
||||||
|
inv.add_user(user)
|
||||||
|
inv.del_user(u'test☭')
|
||||||
|
|
||||||
|
@travis_disabled
|
||||||
|
def testModifyUser(self):
|
||||||
|
inv = Backend(cfg, cherrypy.log, u'test☭', attr, 'sAMAccountName')
|
||||||
|
user = {
|
||||||
|
'sAMAccountName': u'test☭',
|
||||||
|
'sn': u'test☭',
|
||||||
|
'cn': u'test☭',
|
||||||
|
'unicodePwd': u'test☭Aowo87',
|
||||||
|
'uidNumber': '42',
|
||||||
|
'gidNumber': '42',
|
||||||
|
'homeDirectory': '/home/test/'
|
||||||
|
}
|
||||||
|
inv.add_user(user)
|
||||||
|
inv.set_attrs(u'test☭', {'gecos': 'test2', 'homeDirectory': '/home/test/'})
|
||||||
|
inv.del_user(u'test☭')
|
||||||
|
|
||||||
|
@travis_disabled
|
||||||
|
def testAddUserDuplicate(self):
|
||||||
|
inv = Backend(cfg, cherrypy.log, u'test☭', attr, 'sAMAccountName')
|
||||||
|
user = {
|
||||||
|
'sAMAccountName': u'test☭',
|
||||||
|
'sn': u'test☭',
|
||||||
|
'cn': u'test☭',
|
||||||
|
'uidNumber': '42',
|
||||||
|
'unicodePwd': u'test☭aqosJK87',
|
||||||
|
'gidNumber': '42',
|
||||||
|
'homeDirectory': '/home/test/'
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
inv.add_user(user.copy())
|
||||||
|
inv.add_user(user.copy())
|
||||||
|
except UserAlreadyExists:
|
||||||
|
inv.del_user(u'test☭')
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
inv.del_user(u'test☭')
|
||||||
|
raise AssertionError("expected an exception")
|
||||||
|
|
||||||
|
@travis_disabled
|
||||||
|
def testDelUserDontExists(self):
|
||||||
|
inv = Backend(cfg, cherrypy.log, u'test☭', attr, 'sAMAccountName')
|
||||||
|
try:
|
||||||
|
inv.del_user(u'test☭')
|
||||||
|
inv.del_user(u'test☭')
|
||||||
|
except UserDoesntExist:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
raise AssertionError("expected an exception")
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
@ -6,12 +6,14 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import sys
|
import sys
|
||||||
from sets import Set
|
|
||||||
from ldapcherry.backend.backendDemo import Backend
|
from ldapcherry.backend.backendDemo import Backend
|
||||||
from ldapcherry.exceptions import *
|
from ldapcherry.exceptions import *
|
||||||
from disable import travis_disabled
|
from disable import travis_disabled
|
||||||
import cherrypy
|
import cherrypy
|
||||||
import logging
|
import logging
|
||||||
|
if sys.version < '3':
|
||||||
|
from sets import Set as set
|
||||||
|
|
||||||
|
|
||||||
cfg = {
|
cfg = {
|
||||||
'display_name': 'test',
|
'display_name': 'test',
|
||||||
@ -69,16 +71,6 @@ class TestError(object):
|
|||||||
res = inv.auth('notauser', 'password') or inv.auth('default_user', 'notapassword')
|
res = inv.auth('notauser', 'password') or inv.auth('default_user', 'notapassword')
|
||||||
assert res == False
|
assert res == False
|
||||||
|
|
||||||
def testMissingParam(self):
|
|
||||||
cfg2 = {}
|
|
||||||
return True
|
|
||||||
try:
|
|
||||||
inv = Backend(cfg2, cherrypy.log, 'test', attr, 'uid')
|
|
||||||
except MissingKey:
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
raise AssertionError("expected an exception")
|
|
||||||
|
|
||||||
def testGetUser(self):
|
def testGetUser(self):
|
||||||
inv = Backend(cfg, cherrypy.log, 'test', attr, 'uid')
|
inv = Backend(cfg, cherrypy.log, 'test', attr, 'uid')
|
||||||
inv.add_user(default_user)
|
inv.add_user(default_user)
|
||||||
@ -92,7 +84,7 @@ class TestError(object):
|
|||||||
inv.add_user(default_user)
|
inv.add_user(default_user)
|
||||||
inv.add_to_groups('default_user', default_groups)
|
inv.add_to_groups('default_user', default_groups)
|
||||||
ret = inv.get_groups('default_user')
|
ret = inv.get_groups('default_user')
|
||||||
expected = Set(default_groups)
|
expected = set(default_groups)
|
||||||
assert ret == expected
|
assert ret == expected
|
||||||
|
|
||||||
def testSearchUser(self):
|
def testSearchUser(self):
|
||||||
@ -101,7 +93,7 @@ class TestError(object):
|
|||||||
inv.add_user(default_user2)
|
inv.add_user(default_user2)
|
||||||
ret = inv.search('default')
|
ret = inv.search('default')
|
||||||
expected = ['default_user', 'default_user2']
|
expected = ['default_user', 'default_user2']
|
||||||
assert Set(ret.keys()) == Set(expected)
|
assert set(ret.keys()) == set(expected)
|
||||||
|
|
||||||
def testAddUser(self):
|
def testAddUser(self):
|
||||||
try:
|
try:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
@ -6,13 +6,14 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import sys
|
import sys
|
||||||
from sets import Set
|
|
||||||
from ldapcherry.backend.backendLdap import Backend, CaFileDontExist
|
from ldapcherry.backend.backendLdap import Backend, CaFileDontExist
|
||||||
from ldapcherry.exceptions import *
|
from ldapcherry.exceptions import *
|
||||||
from disable import travis_disabled
|
from disable import travis_disabled
|
||||||
import cherrypy
|
import cherrypy
|
||||||
import logging
|
import logging
|
||||||
import ldap
|
import ldap
|
||||||
|
if sys.version < '3':
|
||||||
|
from sets import Set as set
|
||||||
|
|
||||||
cfg = {
|
cfg = {
|
||||||
'module' : 'ldapcherry.backend.ldap',
|
'module' : 'ldapcherry.backend.ldap',
|
||||||
@ -104,7 +105,8 @@ class TestError(object):
|
|||||||
try:
|
try:
|
||||||
ldapc.simple_bind_s(inv.binddn, inv.bindpassword)
|
ldapc.simple_bind_s(inv.binddn, inv.bindpassword)
|
||||||
except ldap.SERVER_DOWN as e:
|
except ldap.SERVER_DOWN as e:
|
||||||
assert e[0]['info'] == 'TLS: hostname does not match CN in peer certificate'
|
assert e.args[0]['info'] == 'TLS: hostname does not match CN in peer certificate' or \
|
||||||
|
e.args[0]['info'] == '(unknown error code)'
|
||||||
else:
|
else:
|
||||||
raise AssertionError("expected an exception")
|
raise AssertionError("expected an exception")
|
||||||
|
|
||||||
@ -128,16 +130,6 @@ class TestError(object):
|
|||||||
res = inv.auth('notauser', 'password') or inv.auth(u'jwatsoné', 'notapasswordé')
|
res = inv.auth('notauser', 'password') or inv.auth(u'jwatsoné', 'notapasswordé')
|
||||||
assert res == False
|
assert res == False
|
||||||
|
|
||||||
def testMissingParam(self):
|
|
||||||
cfg2 = {}
|
|
||||||
return True
|
|
||||||
try:
|
|
||||||
inv = Backend(cfg2, cherrypy.log, 'ldap', attr, 'uid')
|
|
||||||
except MissingKey:
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
raise AssertionError("expected an exception")
|
|
||||||
|
|
||||||
def testGetUser(self):
|
def testGetUser(self):
|
||||||
inv = Backend(cfg, cherrypy.log, 'ldap', attr, 'uid')
|
inv = Backend(cfg, cherrypy.log, 'ldap', attr, 'uid')
|
||||||
ret = inv.get_user(u'jwatsoné')
|
ret = inv.get_user(u'jwatsoné')
|
||||||
@ -158,12 +150,10 @@ class TestError(object):
|
|||||||
]
|
]
|
||||||
inv.add_to_groups(u'jwatsoné', groups)
|
inv.add_to_groups(u'jwatsoné', groups)
|
||||||
ret = inv.get_groups(u'jwatsoné')
|
ret = inv.get_groups(u'jwatsoné')
|
||||||
print ret
|
|
||||||
inv.del_from_groups(u'jwatsoné', ['cn=hrpeople,ou=Groups,dc=example,dc=org'])
|
inv.del_from_groups(u'jwatsoné', ['cn=hrpeople,ou=Groups,dc=example,dc=org'])
|
||||||
inv.del_from_groups(u'jwatsoné', ['cn=hrpeople,ou=Groups,dc=example,dc=org'])
|
inv.del_from_groups(u'jwatsoné', ['cn=hrpeople,ou=Groups,dc=example,dc=org'])
|
||||||
assert ret == ['cn=itpeople,ou=Groups,dc=example,dc=org', 'cn=hrpeople,ou=Groups,dc=example,dc=org']
|
assert ret == ['cn=itpeople,ou=Groups,dc=example,dc=org', 'cn=hrpeople,ou=Groups,dc=example,dc=org']
|
||||||
|
|
||||||
|
|
||||||
def testSearchUser(self):
|
def testSearchUser(self):
|
||||||
inv = Backend(cfg, cherrypy.log, 'ldap', attr, 'uid')
|
inv = Backend(cfg, cherrypy.log, 'ldap', attr, 'uid')
|
||||||
ret = inv.search('smith')
|
ret = inv.search('smith')
|
||||||
@ -172,29 +162,29 @@ class TestError(object):
|
|||||||
|
|
||||||
def testAddUser(self):
|
def testAddUser(self):
|
||||||
try:
|
try:
|
||||||
inv.del_user(u'test☭')
|
inv.del_user(u'test☭,cn=')
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
inv = Backend(cfg, cherrypy.log, 'ldap', attr, 'uid')
|
inv = Backend(cfg, cherrypy.log, 'ldap', attr, 'uid')
|
||||||
user = {
|
user = {
|
||||||
'uid': u'test☭',
|
'uid': u'test☭,cn=',
|
||||||
'sn': 'test',
|
'sn': u'test☭',
|
||||||
'cn': 'test',
|
'cn': u'test☭',
|
||||||
'userPassword': 'test',
|
'userPassword': u'test☭',
|
||||||
'uidNumber': '42',
|
'uidNumber': '42',
|
||||||
'gidNumber': '42',
|
'gidNumber': '42',
|
||||||
'homeDirectory': '/home/test/'
|
'homeDirectory': '/home/test/'
|
||||||
}
|
}
|
||||||
inv.add_user(user)
|
inv.add_user(user)
|
||||||
inv.del_user(u'test☭')
|
inv.del_user(u'test☭,cn=')
|
||||||
|
|
||||||
def testModifyUser(self):
|
def testModifyUser(self):
|
||||||
inv = Backend(cfg, cherrypy.log, 'ldap', attr, 'uid')
|
inv = Backend(cfg, cherrypy.log, 'ldap', attr, 'uid')
|
||||||
user = {
|
user = {
|
||||||
'uid': u'test☭',
|
'uid': u'test☭',
|
||||||
'sn': 'test',
|
'sn': u'test☭',
|
||||||
'cn': 'test',
|
'cn': u'test☭',
|
||||||
'userPassword': 'test',
|
'userPassword': u'test☭',
|
||||||
'uidNumber': '42',
|
'uidNumber': '42',
|
||||||
'gidNumber': '42',
|
'gidNumber': '42',
|
||||||
'homeDirectory': '/home/test/'
|
'homeDirectory': '/home/test/'
|
||||||
@ -206,11 +196,11 @@ class TestError(object):
|
|||||||
def testAddUserDuplicate(self):
|
def testAddUserDuplicate(self):
|
||||||
inv = Backend(cfg, cherrypy.log, 'ldap', attr, 'uid')
|
inv = Backend(cfg, cherrypy.log, 'ldap', attr, 'uid')
|
||||||
user = {
|
user = {
|
||||||
'uid': 'test',
|
'uid': u'test☭',
|
||||||
'sn': 'test',
|
'sn': u'test☭',
|
||||||
'cn': 'test',
|
'cn': u'test☭',
|
||||||
'uidNumber': '42',
|
'uidNumber': '42',
|
||||||
'userPassword': 'test',
|
'userPassword': u'test☭',
|
||||||
'gidNumber': '42',
|
'gidNumber': '42',
|
||||||
'homeDirectory': '/home/test/'
|
'homeDirectory': '/home/test/'
|
||||||
}
|
}
|
||||||
@ -218,17 +208,17 @@ class TestError(object):
|
|||||||
inv.add_user(user)
|
inv.add_user(user)
|
||||||
inv.add_user(user)
|
inv.add_user(user)
|
||||||
except UserAlreadyExists:
|
except UserAlreadyExists:
|
||||||
inv.del_user('test')
|
inv.del_user(u'test☭')
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
inv.del_user('test')
|
inv.del_user(u'test☭')
|
||||||
raise AssertionError("expected an exception")
|
raise AssertionError("expected an exception")
|
||||||
|
|
||||||
def testDelUserDontExists(self):
|
def testDelUserDontExists(self):
|
||||||
inv = Backend(cfg, cherrypy.log, 'ldap', attr, 'uid')
|
inv = Backend(cfg, cherrypy.log, 'ldap', attr, 'uid')
|
||||||
try:
|
try:
|
||||||
inv.del_user('test')
|
inv.del_user(u'test☭')
|
||||||
inv.del_user('test')
|
inv.del_user(u'test☭')
|
||||||
except UserDoesntExist:
|
except UserDoesntExist:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
@ -243,10 +233,10 @@ class TestError(object):
|
|||||||
def testAddUserMissingMustattribute(self):
|
def testAddUserMissingMustattribute(self):
|
||||||
inv = Backend(cfg, cherrypy.log, 'ldap', attr, 'uid')
|
inv = Backend(cfg, cherrypy.log, 'ldap', attr, 'uid')
|
||||||
user = {
|
user = {
|
||||||
'uid': 'test',
|
'uid': u'test☭',
|
||||||
'sn': 'test',
|
'sn': u'test☭',
|
||||||
'cn': 'test',
|
'cn': u'test☭',
|
||||||
'userPassword': 'test',
|
'userPassword': u'test☭',
|
||||||
'gidNumber': '42',
|
'gidNumber': '42',
|
||||||
'homeDirectory': '/home/test/'
|
'homeDirectory': '/home/test/'
|
||||||
}
|
}
|
||||||
@ -255,5 +245,5 @@ class TestError(object):
|
|||||||
except ldap.OBJECT_CLASS_VIOLATION:
|
except ldap.OBJECT_CLASS_VIOLATION:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
inv.del_user('test')
|
inv.del_user(u'test☭')
|
||||||
raise AssertionError("expected an exception")
|
raise AssertionError("expected an exception")
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import sys
|
import sys
|
||||||
@ -10,19 +9,44 @@ import subprocess
|
|||||||
from tempfile import NamedTemporaryFile as tempfile
|
from tempfile import NamedTemporaryFile as tempfile
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from sets import Set
|
|
||||||
from ldapcherry import LdapCherry
|
from ldapcherry import LdapCherry
|
||||||
from ldapcherry.exceptions import *
|
from ldapcherry.exceptions import *
|
||||||
from ldapcherry.pyyamlwrapper import DumplicatedKey, RelationError
|
from ldapcherry.pyyamlwrapper import DumplicatedKey, RelationError
|
||||||
|
import ldapcherry.backend.backendAD
|
||||||
import cherrypy
|
import cherrypy
|
||||||
from cherrypy.process import plugins, servers
|
from cherrypy.process import plugins, servers
|
||||||
from cherrypy import Application
|
from cherrypy import Application
|
||||||
import logging
|
import logging
|
||||||
from ldapcherry.lclogging import *
|
from ldapcherry.lclogging import *
|
||||||
|
from disable import *
|
||||||
import json
|
import json
|
||||||
|
from tidylib import tidy_document
|
||||||
|
if sys.version < '3':
|
||||||
|
from sets import Set as set
|
||||||
|
|
||||||
cherrypy.session = {}
|
cherrypy.session = {}
|
||||||
|
|
||||||
|
adcfg = {
|
||||||
|
'display_name': u'test☭',
|
||||||
|
'domain': 'DC.LDAPCHERRY.ORG',
|
||||||
|
'login': 'Administrator',
|
||||||
|
'password': 'qwertyP455',
|
||||||
|
'uri': 'ldaps://ad.ldapcherry.org',
|
||||||
|
'checkcert': 'off',
|
||||||
|
}
|
||||||
|
adattr = ['shell', 'cn', 'sAMAccountName', 'uidNumber', 'gidNumber', 'home', 'unicodePwd', 'givenName', 'email', 'sn']
|
||||||
|
|
||||||
|
|
||||||
|
addefault_user = {
|
||||||
|
'sAMAccountName': u'☭default_user',
|
||||||
|
'sn': u'test☭1',
|
||||||
|
'cn': u'test☭2',
|
||||||
|
'unicodePwd': u'test☭P666',
|
||||||
|
'uidNumber': '42',
|
||||||
|
'gidNumber': '42',
|
||||||
|
'homeDirectory': '/home/test/'
|
||||||
|
}
|
||||||
|
|
||||||
# monkey patching cherrypy to disable config interpolation
|
# monkey patching cherrypy to disable config interpolation
|
||||||
def new_as_dict(self, raw=True, vars=None):
|
def new_as_dict(self, raw=True, vars=None):
|
||||||
"""Convert an INI file to a dictionary"""
|
"""Convert an INI file to a dictionary"""
|
||||||
@ -56,19 +80,42 @@ class HtmlValidationFailed(Exception):
|
|||||||
def __init__(self, out):
|
def __init__(self, out):
|
||||||
self.errors = out
|
self.errors = out
|
||||||
|
|
||||||
|
def _is_html_error(line):
|
||||||
|
for p in [
|
||||||
|
r'.*Warning: trimming empty <span>.*',
|
||||||
|
r'.*Error: <nav> is not recognized!.*',
|
||||||
|
r'.*Warning: discarding unexpected <nav>.*',
|
||||||
|
r'.*Warning: discarding unexpected </nav>.*',
|
||||||
|
r'.*Warning: <meta> proprietary attribute "charset".*',
|
||||||
|
r'.*Warning: <meta> lacks "content" attribute.*',
|
||||||
|
r'.*Warning: <link> inserting "type" attribute.*',
|
||||||
|
r'.*Warning: <link> proprietary attribute.*',
|
||||||
|
r'.*Warning: <input> proprietary attribute.*',
|
||||||
|
r'.*Warning: <button> proprietary attribute.*',
|
||||||
|
r'.*Warning: <form> proprietary attribute.*',
|
||||||
|
r'.*Warning: <table> lacks "summary" attribute.*',
|
||||||
|
r'.*Warning: <script> inserting "type" attribute.*',
|
||||||
|
r'.*Warning: <input> attribute "id" has invalid value.*',
|
||||||
|
r'.*Warning: <a> proprietary attribute.*',
|
||||||
|
r'.*Warning: <input> attribute "type" has invalid value.*',
|
||||||
|
r'.*Warning: <span> proprietary attribute.*',
|
||||||
|
]:
|
||||||
|
if re.match(p, line):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def htmlvalidator(page):
|
def htmlvalidator(page):
|
||||||
|
document, errors = tidy_document(page,
|
||||||
|
options={'numeric-entities':1})
|
||||||
f = tempfile()
|
f = tempfile()
|
||||||
stdout = tempfile()
|
for line in errors.splitlines():
|
||||||
f.write(page.encode("utf-8"))
|
if _is_html_error(line):
|
||||||
f.seek(0)
|
print("################")
|
||||||
ret = subprocess.call(['./tests/html_validator.py', '-h', f.name], stdout=stdout)
|
print("Blocking error: '%s'" % line)
|
||||||
stdout.seek(0)
|
print("all tidy_document errors:")
|
||||||
out = stdout.read()
|
print(errors)
|
||||||
f.close()
|
print("################")
|
||||||
stdout.close()
|
raise HtmlValidationFailed(line)
|
||||||
print(out)
|
|
||||||
if not re.search(r'Error:.*', out) is None:
|
|
||||||
raise HtmlValidationFailed(out)
|
|
||||||
|
|
||||||
class BadModule():
|
class BadModule():
|
||||||
pass
|
pass
|
||||||
@ -105,7 +152,7 @@ class TestError(object):
|
|||||||
def testLog(self):
|
def testLog(self):
|
||||||
app = LdapCherry()
|
app = LdapCherry()
|
||||||
cfg = { 'global' : {}}
|
cfg = { 'global' : {}}
|
||||||
for t in ['none', 'file', 'syslog']:
|
for t in ['none', 'file', 'syslog', 'stdout']:
|
||||||
cfg['global']['log.access_handler']=t
|
cfg['global']['log.access_handler']=t
|
||||||
cfg['global']['log.error_handler']=t
|
cfg['global']['log.error_handler']=t
|
||||||
app._set_access_log(cfg, logging.DEBUG)
|
app._set_access_log(cfg, logging.DEBUG)
|
||||||
@ -159,10 +206,10 @@ class TestError(object):
|
|||||||
loadconf('./tests/cfg/ldapcherry_test.ini', app)
|
loadconf('./tests/cfg/ldapcherry_test.ini', app)
|
||||||
app.auth_mode = 'or'
|
app.auth_mode = 'or'
|
||||||
try:
|
try:
|
||||||
app.login('jwatsoné', 'passwordwatsoné')
|
app.login(u'jwatsoné', u'passwordwatsoné')
|
||||||
except cherrypy.HTTPRedirect as e:
|
except cherrypy.HTTPRedirect as e:
|
||||||
expected = 'http://127.0.0.1:8080/'
|
expected = 'http://127.0.0.1:8080/'
|
||||||
assert e[0][0] == expected
|
assert e.urls[0] == expected
|
||||||
else:
|
else:
|
||||||
raise AssertionError("expected an exception")
|
raise AssertionError("expected an exception")
|
||||||
|
|
||||||
@ -171,10 +218,10 @@ class TestError(object):
|
|||||||
loadconf('./tests/cfg/ldapcherry_test.ini', app)
|
loadconf('./tests/cfg/ldapcherry_test.ini', app)
|
||||||
app.auth_mode = 'or'
|
app.auth_mode = 'or'
|
||||||
try:
|
try:
|
||||||
app.login('jwatsoné', 'wrongPasswordé')
|
app.login(u'jwatsoné', u'wrongPasswordé')
|
||||||
except cherrypy.HTTPRedirect as e:
|
except cherrypy.HTTPRedirect as e:
|
||||||
expected = 'http://127.0.0.1:8080/signin'
|
expected = 'http://127.0.0.1:8080/signin'
|
||||||
assert e[0][0] == expected
|
assert e.urls[0] == expected
|
||||||
else:
|
else:
|
||||||
raise AssertionError("expected an exception")
|
raise AssertionError("expected an exception")
|
||||||
|
|
||||||
@ -246,6 +293,7 @@ class TestError(object):
|
|||||||
app._modify(modify_form)
|
app._modify(modify_form)
|
||||||
app._deleteuser('test')
|
app._deleteuser('test')
|
||||||
|
|
||||||
|
@slow_disabled
|
||||||
def testHtml(self):
|
def testHtml(self):
|
||||||
app = LdapCherry()
|
app = LdapCherry()
|
||||||
loadconf('./tests/cfg/ldapcherry_test.ini', app)
|
loadconf('./tests/cfg/ldapcherry_test.ini', app)
|
||||||
@ -265,8 +313,14 @@ class TestError(object):
|
|||||||
def testNoneType(self):
|
def testNoneType(self):
|
||||||
app = LdapCherry()
|
app = LdapCherry()
|
||||||
loadconf('./tests/cfg/ldapcherry_test.ini', app)
|
loadconf('./tests/cfg/ldapcherry_test.ini', app)
|
||||||
app.modify('ssmith'),
|
app.modify('ssmith')
|
||||||
|
|
||||||
|
def testNoneModify(self):
|
||||||
|
app = LdapCherry()
|
||||||
|
loadconf('./tests/cfg/ldapcherry_test.ini', app)
|
||||||
|
app.modify(user=None)
|
||||||
|
|
||||||
|
@slow_disabled
|
||||||
def testNaughtyStrings(self):
|
def testNaughtyStrings(self):
|
||||||
app = LdapCherry()
|
app = LdapCherry()
|
||||||
loadconf('./tests/cfg/ldapcherry_test.ini', app)
|
loadconf('./tests/cfg/ldapcherry_test.ini', app)
|
||||||
@ -285,6 +339,40 @@ class TestError(object):
|
|||||||
app._deleteuser('test')
|
app._deleteuser('test')
|
||||||
htmlvalidator(page[0])
|
htmlvalidator(page[0])
|
||||||
|
|
||||||
|
@travis_disabled
|
||||||
|
def testDeleteUserOneBackend(self):
|
||||||
|
app = LdapCherry()
|
||||||
|
loadconf('./tests/cfg/ldapcherry_adldap.cfg', app)
|
||||||
|
inv = ldapcherry.backend.backendAD.Backend(adcfg, cherrypy.log, u'test☭', adattr, 'sAMAccountName')
|
||||||
|
inv.add_user(addefault_user.copy())
|
||||||
|
app._deleteuser(u'☭default_user')
|
||||||
|
|
||||||
|
@travis_disabled
|
||||||
|
def testAddUserOneBackend(self):
|
||||||
|
app = LdapCherry()
|
||||||
|
loadconf('./tests/cfg/ldapcherry_adldap.cfg', app)
|
||||||
|
inv = ldapcherry.backend.backendAD.Backend(adcfg, cherrypy.log, u'test☭', adattr, 'sAMAccountName')
|
||||||
|
inv.add_user(addefault_user.copy())
|
||||||
|
form = {'groups': {}, 'attrs': {'password1': u'password☭P455', 'password2': u'password☭P455', 'cn': u'Test ☭ Test', 'name': u'Test ☭', 'uidNumber': u'1000', 'gidNumber': u'1000', 'home': u'/home/test', 'first-name': u'Test ☭', 'email': u'test@test.fr', 'uid': u'☭default_user'}, 'roles': {'admin-lv3': u'on', 'admin-lv2': u'on', 'users': u'on'}}
|
||||||
|
app._adduser(form)
|
||||||
|
app._deleteuser(u'☭default_user')
|
||||||
|
|
||||||
|
@travis_disabled
|
||||||
|
def testModifyUserOneBackend(self):
|
||||||
|
app = LdapCherry()
|
||||||
|
loadconf('./tests/cfg/ldapcherry_adldap.cfg', app)
|
||||||
|
inv = ldapcherry.backend.backendAD.Backend(adcfg, cherrypy.log, u'test☭', adattr, 'sAMAccountName')
|
||||||
|
try:
|
||||||
|
app._deleteuser(u'☭default_user')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
inv.add_user(addefault_user.copy())
|
||||||
|
modify_form = { 'attrs': {'first-name': u'Test42 ☭', 'uid': u'☭default_user'}, 'roles': { 'admin-lv3': u'on'}}
|
||||||
|
app._modify(modify_form)
|
||||||
|
app._deleteuser(u'☭default_user')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def testLogger(self):
|
def testLogger(self):
|
||||||
app = LdapCherry()
|
app = LdapCherry()
|
||||||
loadconf('./tests/cfg/ldapcherry.ini', app)
|
loadconf('./tests/cfg/ldapcherry.ini', app)
|
||||||
@ -297,4 +385,3 @@ class TestError(object):
|
|||||||
get_loglevel('alert') is logging.CRITICAL and \
|
get_loglevel('alert') is logging.CRITICAL and \
|
||||||
get_loglevel('emergency') is logging.CRITICAL and \
|
get_loglevel('emergency') is logging.CRITICAL and \
|
||||||
get_loglevel('notalevel') is logging.INFO
|
get_loglevel('notalevel') is logging.INFO
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
@ -6,16 +6,17 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import sys
|
import sys
|
||||||
from sets import Set
|
|
||||||
from ldapcherry.roles import Roles
|
from ldapcherry.roles import Roles
|
||||||
from ldapcherry.exceptions import DumplicateRoleKey, MissingKey, DumplicateRoleContent, MissingRolesFile, MissingRole
|
from ldapcherry.exceptions import DumplicateRoleKey, MissingKey, DumplicateRoleContent, MissingRolesFile, MissingRole
|
||||||
from ldapcherry.pyyamlwrapper import DumplicatedKey, RelationError
|
from ldapcherry.pyyamlwrapper import DumplicatedKey, RelationError
|
||||||
|
if sys.version < '3':
|
||||||
|
from sets import Set as set
|
||||||
|
|
||||||
class TestError(object):
|
class TestError(object):
|
||||||
|
|
||||||
def testNominal(self):
|
def testNominal(self):
|
||||||
inv = Roles('./tests/cfg/roles.yml')
|
inv = Roles('./tests/cfg/roles.yml')
|
||||||
print inv.roles
|
print(inv.roles)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def testMissingDisplayName(self):
|
def testMissingDisplayName(self):
|
||||||
@ -64,7 +65,7 @@ class TestError(object):
|
|||||||
['admin-lv2', 'admin-lv3', 'users'],
|
['admin-lv2', 'admin-lv3', 'users'],
|
||||||
['admin-lv2']
|
['admin-lv2']
|
||||||
)
|
)
|
||||||
expected = {'ad': Set(['Administrators', 'Domain Controllers']), 'ldap': Set(['cn=nagios admins,ou=group,dc=example,dc=com', 'cn=puppet admins,ou=group,dc=example,dc=com', 'cn=dns admins,ou=group,dc=example,dc=com'])}
|
expected = {'ad': set(['Administrators', 'Domain Controllers']), 'ldap': set(['cn=nagios admins,ou=group,dc=example,dc=com', 'cn=puppet admins,ou=group,dc=example,dc=com', 'cn=dns admins,ou=group,dc=example,dc=com'])}
|
||||||
assert groups == expected
|
assert groups == expected
|
||||||
|
|
||||||
def testGetGroup(self):
|
def testGetGroup(self):
|
||||||
@ -78,7 +79,50 @@ class TestError(object):
|
|||||||
|
|
||||||
def testNested(self):
|
def testNested(self):
|
||||||
inv = Roles('./tests/cfg/nested.yml')
|
inv = Roles('./tests/cfg/nested.yml')
|
||||||
expected = {'developpers': {'backends_groups': {'ad': ['Domain Users'], 'ldap': ['cn=developpers,ou=group,dc=example,dc=com', 'cn=users,ou=group,dc=example,dc=com']}, 'display_name': 'Developpers', 'description': 'description'}, 'admin-lv3': {'backends_groups': {'ad': ['Domain Users', 'Administrators', 'Domain Controllers'], 'ldap': ['cn=nagios admins,ou=group,dc=example,dc=com', 'cn=users,ou=group,dc=example,dc=com', 'cn=puppet admins,ou=group,dc=example,dc=com', 'cn=dns admins,ou=group,dc=example,dc=com']}, 'display_name': 'Administrators Level 3', 'description': 'description'}, 'admin-lv2': {'backends_groups': {'ad': ['Domain Users'], 'ldap': ['cn=nagios admins,ou=group,dc=example,dc=com', 'cn=users,ou=group,dc=example,dc=com']}, 'display_name': 'Administrators Level 2', 'description': 'description', 'LC_admins': True}, 'users': {'backends_groups': {'ad': ['Domain Users'], 'ldap': ['cn=users,ou=group,dc=example,dc=com']}, 'display_name': 'Simple Users', 'description': 'description'}}
|
expected = {
|
||||||
|
'admin-lv2': {
|
||||||
|
'LC_admins': True,
|
||||||
|
'backends_groups': {
|
||||||
|
'ad': ['Domain Users'],
|
||||||
|
'ldap': ['cn=nagios '
|
||||||
|
'admins,ou=group,dc=example,dc=com',
|
||||||
|
'cn=users,ou=group,dc=example,dc=com']
|
||||||
|
},
|
||||||
|
'description': 'description',
|
||||||
|
'display_name': 'Administrators Level 2'
|
||||||
|
},
|
||||||
|
'admin-lv3': {
|
||||||
|
'backends_groups': {
|
||||||
|
'ad': ['Administrators',
|
||||||
|
'Domain Controllers',
|
||||||
|
'Domain Users'],
|
||||||
|
'ldap': ['cn=dns '
|
||||||
|
'admins,ou=group,dc=example,dc=com',
|
||||||
|
'cn=nagios '
|
||||||
|
'admins,ou=group,dc=example,dc=com',
|
||||||
|
'cn=puppet '
|
||||||
|
'admins,ou=group,dc=example,dc=com',
|
||||||
|
'cn=users,ou=group,dc=example,dc=com']
|
||||||
|
},
|
||||||
|
'description': 'description',
|
||||||
|
'display_name': 'Administrators Level 3'
|
||||||
|
},
|
||||||
|
'developers': {
|
||||||
|
'backends_groups': {
|
||||||
|
'ad': ['Domain Users'],
|
||||||
|
'ldap': ['cn=developers,ou=group,dc=example,dc=com',
|
||||||
|
'cn=users,ou=group,dc=example,dc=com']},
|
||||||
|
'description': 'description',
|
||||||
|
'display_name': 'Developpers'
|
||||||
|
},
|
||||||
|
'users': {
|
||||||
|
'backends_groups': {
|
||||||
|
'ad': ['Domain Users'],
|
||||||
|
'ldap': ['cn=users,ou=group,dc=example,dc=com']},
|
||||||
|
'description': 'description',
|
||||||
|
'display_name': 'Simple Users'
|
||||||
|
}
|
||||||
|
}
|
||||||
assert expected == inv.flatten
|
assert expected == inv.flatten
|
||||||
|
|
||||||
def testGetGroupMissingRole(self):
|
def testGetGroupMissingRole(self):
|
||||||
@ -108,13 +152,13 @@ class TestError(object):
|
|||||||
def testGetAllRoles(self):
|
def testGetAllRoles(self):
|
||||||
inv = Roles('./tests/cfg/roles.yml')
|
inv = Roles('./tests/cfg/roles.yml')
|
||||||
res = inv.get_allroles()
|
res = inv.get_allroles()
|
||||||
expected = ['developpers', 'admin-lv3', 'admin-lv2', 'users']
|
expected = ['developers', 'admin-lv3', 'admin-lv2', 'users']
|
||||||
assert res == expected
|
assert res == expected
|
||||||
|
|
||||||
def testGetAllRoles(self):
|
def testGetAllRoles(self):
|
||||||
inv = Roles('./tests/cfg/roles.yml')
|
inv = Roles('./tests/cfg/roles.yml')
|
||||||
res = inv.get_backends()
|
res = inv.get_backends()
|
||||||
expected = Set(['ad', 'ldap'])
|
expected = set(['ad', 'ldap'])
|
||||||
assert res == expected
|
assert res == expected
|
||||||
|
|
||||||
def testDumpNested(self):
|
def testDumpNested(self):
|
||||||
@ -143,9 +187,9 @@ class TestError(object):
|
|||||||
'ad' : ['Domain Users', 'Domain Users 2'],
|
'ad' : ['Domain Users', 'Domain Users 2'],
|
||||||
'ldap': ['cn=users,ou=group,dc=example,dc=com',
|
'ldap': ['cn=users,ou=group,dc=example,dc=com',
|
||||||
'cn=nagios admins,ou=group,dc=example,dc=com',
|
'cn=nagios admins,ou=group,dc=example,dc=com',
|
||||||
'cn=developpers,ou=group,dc=example,dc=com',
|
'cn=developers,ou=group,dc=example,dc=com',
|
||||||
],
|
],
|
||||||
'toto': ['not a group'],
|
'toto': ['not a group'],
|
||||||
}
|
}
|
||||||
expected = {'unusedgroups': {'toto': Set(['not a group']), 'ad': Set(['Domain Users 2'])}, 'roles': Set(['developpers', 'admin-lv2', 'users'])}
|
expected = {'unusedgroups': {'toto': set(['not a group']), 'ad': set(['Domain Users 2'])}, 'roles': set(['developers', 'admin-lv2', 'users'])}
|
||||||
assert inv.get_roles(groups) == expected
|
assert inv.get_roles(groups) == expected
|
||||||
|
@ -1,19 +1,27 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get install ldap-utils slapd -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -f -q -y
|
apt update
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get install samba -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -f -q -y
|
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get install w3c-markup-validator -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -f -q -y
|
DEBIAN_FRONTEND=noninteractive apt-get install ldap-utils slapd -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -f -q -y
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install samba-dsdb-modules samba-vfs-modules samba -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -f -q -y
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install winbind -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -f -q -y
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install build-essential python3-dev libsasl2-dev slapd ldap-utils tox lcov valgrind libtidy-dev libldap-dev python3-cherrypy3 python3-ldap python3-mako -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -f -q -y
|
||||||
|
|
||||||
|
[ -e '/etc/default/slapd' ] && rm -rf /etc/default/slapd
|
||||||
|
cp -r `dirname $0`/etc/default/slapd /etc/default/slapd
|
||||||
|
[ -e '/etc/ldap' ] && rm -rf /etc/ldap
|
||||||
|
cp -r `dirname $0`/etc/ldap /etc/ldap
|
||||||
|
[ -e '/etc/ldapcherry' ] && rm -rf /etc/ldapcherry
|
||||||
|
cp -r `dirname $0`/etc/ldapcherry /etc/ldapcherry
|
||||||
|
|
||||||
rsync -a `dirname $0`/ /
|
|
||||||
cd `dirname $0`/../../
|
cd `dirname $0`/../../
|
||||||
sudo sed -i "s%template_dir.*%template_dir = '`pwd`/resources/templates/'%" /etc/ldapcherry/ldapcherry.ini
|
sudo sed -i "s%template_dir.*%template_dir = '`pwd`/resources/templates/'%" /etc/ldapcherry/ldapcherry.ini
|
||||||
sudo sed -i "s%tools.staticdir.dir.*%tools.staticdir.dir = '`pwd`/resources/static/'%" /etc/ldapcherry/ldapcherry.ini
|
sudo sed -i "s%tools.staticdir.dir.*%tools.staticdir.dir = '`pwd`/resources/static/'%" /etc/ldapcherry/ldapcherry.ini
|
||||||
|
|
||||||
chown -R openldap:openldap /etc/ldap/
|
chown -R openldap:openldap /etc/ldap/
|
||||||
rm /etc/ldap/slapd.d/cn\=config/*mdb*
|
|
||||||
/etc/init.d/slapd restart
|
/etc/init.d/slapd restart
|
||||||
ldapadd -c -H ldap://localhost:390 -x -D "cn=admin,dc=example,dc=org" -f /etc/ldap/content.ldif -w password
|
ldapadd -c -H ldap://localhost:390 -x -D "cn=admin,dc=example,dc=org" -f /etc/ldap/content.ldif -w password
|
||||||
if grep -q '127.0.0.1' /etc/hosts
|
if grep -q '127.0.0.1' /etc/hosts && ! grep -q 'ldap.ldapcherry.org' /etc/hosts
|
||||||
then
|
then
|
||||||
sed -i "s/\(127.0.0.1.*\)/\1 ldap.ldapcherry.org ad.ldapcherry.org ldap.dnscherry.org/" /etc/hosts
|
sed -i "s/\(127.0.0.1.*\)/\1 ldap.ldapcherry.org ad.ldapcherry.org ldap.dnscherry.org/" /etc/hosts
|
||||||
else
|
else
|
||||||
@ -24,15 +32,29 @@ cat /etc/hosts
|
|||||||
|
|
||||||
df -h
|
df -h
|
||||||
|
|
||||||
|
find /var/log/samba/ -type f -exec rm -f {} \;
|
||||||
|
|
||||||
smbconffile=/etc/samba/smb.conf
|
smbconffile=/etc/samba/smb.conf
|
||||||
domain=dc
|
domain=dc
|
||||||
realm=dc.ldapcherry.org
|
realm=dc.ldapcherry.org
|
||||||
sambadns=NONE
|
sambadns=SAMBA_INTERNAL
|
||||||
targetdir=/var/lib/samba/
|
targetdir=/var/lib/samba/
|
||||||
role=dc
|
role=dc
|
||||||
sambacmd=samba-tool
|
sambacmd=samba-tool
|
||||||
adpass=qwertyP455
|
adpass=qwertyP455
|
||||||
|
|
||||||
|
systemctl unmask samba-ad-dc
|
||||||
|
|
||||||
|
hostname ad.ldapcherry.org
|
||||||
|
pkill -9 dnsmasq
|
||||||
|
pkill -9 samba
|
||||||
|
|
||||||
|
kill -9 `cat /var/run/samba/smbd.pid`
|
||||||
|
rm -f /var/run/samba/smbd.pid
|
||||||
|
kill -9 `cat /var/run/samba/nmbd.pid`
|
||||||
|
rm -f /var/run/samba/nmbd.pid
|
||||||
|
rm -rf /var/run/samba
|
||||||
|
|
||||||
echo "deploy AD"
|
echo "deploy AD"
|
||||||
printf '' > "${smbconffile}" && \
|
printf '' > "${smbconffile}" && \
|
||||||
${sambacmd} domain provision ${hostip} \
|
${sambacmd} domain provision ${hostip} \
|
||||||
@ -44,18 +66,20 @@ printf '' > "${smbconffile}" && \
|
|||||||
echo "Move configuration"
|
echo "Move configuration"
|
||||||
mv "${targetdir}/etc/smb.conf" "${smbconffile}"
|
mv "${targetdir}/etc/smb.conf" "${smbconffile}"
|
||||||
|
|
||||||
|
cat ${smbconffile}
|
||||||
|
|
||||||
mv /var/lib/samba/private/krb5.conf /etc/krb5.conf
|
mv /var/lib/samba/private/krb5.conf /etc/krb5.conf
|
||||||
|
|
||||||
|
sleep 15
|
||||||
|
|
||||||
sleep 5
|
systemctl restart samba-ad-dc
|
||||||
|
|
||||||
/etc/init.d/samba stop
|
|
||||||
/etc/init.d/smbd stop
|
|
||||||
/etc/init.d/nmbd stop
|
|
||||||
/etc/init.d/samba-ad-dc restart
|
/etc/init.d/samba-ad-dc restart
|
||||||
|
|
||||||
cat /var/log/samba/*
|
cat /var/log/samba/*
|
||||||
|
|
||||||
sleep 5
|
sleep 5
|
||||||
|
|
||||||
netstat -apn
|
samba-tool domain passwordsettings set -d 1 --complexity off
|
||||||
|
samba-tool domain passwordsettings set -d 1 --min-pwd-length 0
|
||||||
|
systemctl status samba-ad-dc
|
||||||
|
ss -apn | grep samba
|
||||||
|
@ -103,9 +103,22 @@ cn: users
|
|||||||
description: Basic Users
|
description: Basic Users
|
||||||
member: cn=Sheri Smith,ou=people,dc=example,dc=org
|
member: cn=Sheri Smith,ou=people,dc=example,dc=org
|
||||||
|
|
||||||
dn: cn=developpers,ou=group,dc=example,dc=org
|
dn: cn=developers,ou=group,dc=example,dc=org
|
||||||
objectclass: groupofnames
|
objectclass: groupofnames
|
||||||
cn: developpers
|
cn: developers
|
||||||
description: Developpers
|
description: Developpers
|
||||||
member: cn=Sheri Smith,ou=people,dc=example,dc=org
|
member: cn=Sheri Smith,ou=people,dc=example,dc=org
|
||||||
|
|
||||||
|
dn: cn=posixdev,ou=group,dc=example,dc=org
|
||||||
|
objectclass: posixGroup
|
||||||
|
cn: posixdev
|
||||||
|
description: Developpers
|
||||||
|
gidNumber: 10002
|
||||||
|
memberUid: ssmith
|
||||||
|
|
||||||
|
dn: cn=posixadm,ou=group,dc=example,dc=org
|
||||||
|
objectclass: posixGroup
|
||||||
|
cn: posixadm
|
||||||
|
description: Administration
|
||||||
|
gidNumber: 10001
|
||||||
|
memberUid: ssmith
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||||
|
# CRC32 7a6099db
|
||||||
dn: cn=config
|
dn: cn=config
|
||||||
objectClass: olcGlobal
|
objectClass: olcGlobal
|
||||||
cn: config
|
cn: config
|
||||||
@ -7,12 +8,12 @@ olcLogLevel: none
|
|||||||
olcPidFile: /var/run/slapd/slapd.pid
|
olcPidFile: /var/run/slapd/slapd.pid
|
||||||
olcToolThreads: 1
|
olcToolThreads: 1
|
||||||
structuralObjectClass: olcGlobal
|
structuralObjectClass: olcGlobal
|
||||||
entryUUID: 2964261c-6754-1033-8d48-1703270f04bd
|
entryUUID: 38579c70-750a-103e-8489-9578878139e2
|
||||||
creatorsName: cn=config
|
creatorsName: cn=config
|
||||||
createTimestamp: 20140503211805Z
|
createTimestamp: 20240312221838Z
|
||||||
entryCSN: 20140503211805.658522Z#000000#000#000000
|
entryCSN: 20240312221838.644900Z#000000#000#000000
|
||||||
modifiersName: cn=config
|
modifiersName: cn=config
|
||||||
modifyTimestamp: 20140503211805Z
|
modifyTimestamp: 20240312221838Z
|
||||||
olcTLSCACertificateFile: /etc/ldap/ssl/TEST-cacert.pem
|
olcTLSCACertificateFile: /etc/ldap/ssl/TEST-cacert.pem
|
||||||
olcTLSCertificateFile: /etc/ldap/ssl/ldap@dnscherry.org-cert.pem
|
olcTLSCertificateFile: /etc/ldap/ssl/ldap@dnscherry.org-cert.pem
|
||||||
olcTLSCertificateKeyFile: /etc/ldap/ssl/ldap@dnscherry.org-key.pem
|
olcTLSCertificateKeyFile: /etc/ldap/ssl/ldap@dnscherry.org-key.pem
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||||
# CRC32 d8758c75
|
# CRC32 baf86138
|
||||||
dn: cn=module{0}
|
dn: cn=module{0}
|
||||||
objectClass: olcModuleList
|
objectClass: olcModuleList
|
||||||
cn: module{0}
|
cn: module{0}
|
||||||
olcModulePath: /usr/lib/ldap
|
olcModulePath: /usr/lib/ldap
|
||||||
olcModuleLoad: {0}back_hdb
|
olcModuleLoad: {0}back_mdb
|
||||||
structuralObjectClass: olcModuleList
|
structuralObjectClass: olcModuleList
|
||||||
entryUUID: 2964fd58-6754-1033-8d50-1703270f04bd
|
entryUUID: 3857cd3a-750a-103e-8491-9578878139e2
|
||||||
creatorsName: cn=admin,cn=config
|
creatorsName: cn=admin,cn=config
|
||||||
createTimestamp: 20140503211805Z
|
createTimestamp: 20240312221838Z
|
||||||
entryCSN: 20140503211805.664149Z#000000#000#000000
|
entryCSN: 20240312221838.646168Z#000000#000#000000
|
||||||
modifiersName: cn=admin,cn=config
|
modifiersName: cn=admin,cn=config
|
||||||
modifyTimestamp: 20140503211805Z
|
modifyTimestamp: 20240312221838Z
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||||
# CRC32 db8c9607
|
# CRC32 fff51a11
|
||||||
dn: cn=schema
|
dn: cn=schema
|
||||||
objectClass: olcSchemaConfig
|
objectClass: olcSchemaConfig
|
||||||
cn: schema
|
cn: schema
|
||||||
structuralObjectClass: olcSchemaConfig
|
structuralObjectClass: olcSchemaConfig
|
||||||
entryUUID: 29644a02-6754-1033-8d4b-1703270f04bd
|
entryUUID: 3857a42c-750a-103e-848c-9578878139e2
|
||||||
creatorsName: cn=admin,cn=config
|
creatorsName: cn=admin,cn=config
|
||||||
createTimestamp: 20140503211805Z
|
createTimestamp: 20240312221838Z
|
||||||
entryCSN: 20140503211805.659557Z#000000#000#000000
|
entryCSN: 20240312221838.645118Z#000000#000#000000
|
||||||
modifiersName: cn=admin,cn=config
|
modifiersName: cn=admin,cn=config
|
||||||
modifyTimestamp: 20140503211805Z
|
modifyTimestamp: 20240312221838Z
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
|
||||||
# CRC32 94d41de9
|
|
||||||
dn: olcBackend={0}hdb
|
|
||||||
objectClass: olcBackendConfig
|
|
||||||
olcBackend: {0}hdb
|
|
||||||
structuralObjectClass: olcBackendConfig
|
|
||||||
entryUUID: 2965a6f4-6754-1033-8d51-1703270f04bd
|
|
||||||
creatorsName: cn=admin,cn=config
|
|
||||||
createTimestamp: 20140503211805Z
|
|
||||||
entryCSN: 20140503211805.668489Z#000000#000#000000
|
|
||||||
modifiersName: cn=admin,cn=config
|
|
||||||
modifyTimestamp: 20140503211805Z
|
|
@ -1,5 +1,5 @@
|
|||||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||||
# CRC32 511a173e
|
# CRC32 e8f0a13c
|
||||||
dn: olcDatabase={-1}frontend
|
dn: olcDatabase={-1}frontend
|
||||||
objectClass: olcDatabaseConfig
|
objectClass: olcDatabaseConfig
|
||||||
objectClass: olcFrontendConfig
|
objectClass: olcFrontendConfig
|
||||||
@ -10,9 +10,9 @@ olcAccess: {1}to dn.exact="" by * read
|
|||||||
olcAccess: {2}to dn.base="cn=Subschema" by * read
|
olcAccess: {2}to dn.base="cn=Subschema" by * read
|
||||||
olcSizeLimit: 500
|
olcSizeLimit: 500
|
||||||
structuralObjectClass: olcDatabaseConfig
|
structuralObjectClass: olcDatabaseConfig
|
||||||
entryUUID: 296430d0-6754-1033-8d49-1703270f04bd
|
entryUUID: 38579ebe-750a-103e-848a-9578878139e2
|
||||||
creatorsName: cn=config
|
creatorsName: cn=config
|
||||||
createTimestamp: 20140503211805Z
|
createTimestamp: 20240312221838Z
|
||||||
entryCSN: 20140503211805.658911Z#000000#000#000000
|
entryCSN: 20240312221838.644978Z#000000#000#000000
|
||||||
modifiersName: cn=config
|
modifiersName: cn=config
|
||||||
modifyTimestamp: 20140503211805Z
|
modifyTimestamp: 20240312221838Z
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||||
# CRC32 fc130d0e
|
# CRC32 caed0a01
|
||||||
dn: olcDatabase={0}config
|
dn: olcDatabase={0}config
|
||||||
objectClass: olcDatabaseConfig
|
objectClass: olcDatabaseConfig
|
||||||
olcDatabase: {0}config
|
olcDatabase: {0}config
|
||||||
@ -7,9 +7,9 @@ olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external
|
|||||||
,cn=auth manage by * break
|
,cn=auth manage by * break
|
||||||
olcRootDN: cn=admin,cn=config
|
olcRootDN: cn=admin,cn=config
|
||||||
structuralObjectClass: olcDatabaseConfig
|
structuralObjectClass: olcDatabaseConfig
|
||||||
entryUUID: 29644142-6754-1033-8d4a-1703270f04bd
|
entryUUID: 3857a274-750a-103e-848b-9578878139e2
|
||||||
creatorsName: cn=config
|
creatorsName: cn=config
|
||||||
createTimestamp: 20140503211805Z
|
createTimestamp: 20240312221838Z
|
||||||
entryCSN: 20140503211805.659332Z#000000#000#000000
|
entryCSN: 20240312221838.645074Z#000000#000#000000
|
||||||
modifiersName: cn=config
|
modifiersName: cn=config
|
||||||
modifyTimestamp: 20140503211805Z
|
modifyTimestamp: 20240312221838Z
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
dn: olcDatabase={1}hdb
|
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||||
|
# CRC32 91d29b33
|
||||||
|
dn: olcDatabase={1}mdb
|
||||||
objectClass: olcDatabaseConfig
|
objectClass: olcDatabaseConfig
|
||||||
objectClass: olcHdbConfig
|
objectClass: olcMdbConfig
|
||||||
olcDatabase: {1}hdb
|
olcDatabase: {1}mdb
|
||||||
olcDbDirectory: /var/lib/ldap
|
olcDbDirectory: /var/lib/ldap
|
||||||
olcSuffix: dc=example,dc=org
|
olcSuffix: dc=example,dc=org
|
||||||
olcAccess: {0}to attrs=userPassword,shadowLastChange by self write by anonymou
|
olcAccess: {0}to attrs=userPassword,shadowLastChange by self write by anonymou
|
||||||
@ -13,15 +15,15 @@ olcLastMod: TRUE
|
|||||||
olcRootDN: cn=admin,dc=example,dc=org
|
olcRootDN: cn=admin,dc=example,dc=org
|
||||||
olcRootPW: {SSHA}Fp+rSxe5eFsj0DGITJts4DwdSDFDZG9P
|
olcRootPW: {SSHA}Fp+rSxe5eFsj0DGITJts4DwdSDFDZG9P
|
||||||
olcDbCheckpoint: 512 30
|
olcDbCheckpoint: 512 30
|
||||||
olcDbConfig: {0}set_cachesize 0 2097152 0
|
|
||||||
olcDbConfig: {1}set_lk_max_objects 1500
|
|
||||||
olcDbConfig: {2}set_lk_max_locks 1500
|
|
||||||
olcDbConfig: {3}set_lk_max_lockers 1500
|
|
||||||
olcDbIndex: objectClass eq
|
olcDbIndex: objectClass eq
|
||||||
structuralObjectClass: olcHdbConfig
|
olcDbIndex: cn,uid eq
|
||||||
entryUUID: 2965af5a-6754-1033-8d52-1703270f04bd
|
olcDbIndex: uidNumber,gidNumber eq
|
||||||
|
olcDbIndex: member,memberUid eq
|
||||||
|
olcDbMaxSize: 1073741824
|
||||||
|
structuralObjectClass: olcMdbConfig
|
||||||
|
entryUUID: 3857d7ee-750a-103e-8492-9578878139e2
|
||||||
creatorsName: cn=admin,cn=config
|
creatorsName: cn=admin,cn=config
|
||||||
createTimestamp: 20140503211805Z
|
createTimestamp: 20240312221838Z
|
||||||
entryCSN: 20140503211805.668708Z#000000#000#000000
|
entryCSN: 20240312221838.646442Z#000000#000#000000
|
||||||
modifiersName: cn=admin,cn=config
|
modifiersName: cn=admin,cn=config
|
||||||
modifyTimestamp: 20140503211805Z
|
modifyTimestamp: 20240312221838Z
|
132
tests/test_env/etc/ldapcherry/attributes.yml
Normal file
132
tests/test_env/etc/ldapcherry/attributes.yml
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
cn:
|
||||||
|
description: "First Name and Display Name"
|
||||||
|
display_name: "Display Name"
|
||||||
|
type: string
|
||||||
|
weight: 30
|
||||||
|
autofill:
|
||||||
|
function: lcDisplayName
|
||||||
|
args:
|
||||||
|
- $first-name
|
||||||
|
- $name
|
||||||
|
backends:
|
||||||
|
ldap: cn
|
||||||
|
ad: cn
|
||||||
|
first-name:
|
||||||
|
description: "First name of the user"
|
||||||
|
display_name: "First Name"
|
||||||
|
search_displayed: True
|
||||||
|
type: string
|
||||||
|
weight: 20
|
||||||
|
backends:
|
||||||
|
ldap: givenName
|
||||||
|
ad: givenName
|
||||||
|
name:
|
||||||
|
description: "Family name of the user"
|
||||||
|
display_name: "Name"
|
||||||
|
search_displayed: True
|
||||||
|
weight: 10
|
||||||
|
type: string
|
||||||
|
backends:
|
||||||
|
ldap: sn
|
||||||
|
ad: sn
|
||||||
|
email:
|
||||||
|
description: "Email of the user"
|
||||||
|
display_name: "Email"
|
||||||
|
search_displayed: True
|
||||||
|
type: email
|
||||||
|
weight: 40
|
||||||
|
autofill:
|
||||||
|
function: lcMail
|
||||||
|
args:
|
||||||
|
- $first-name
|
||||||
|
- $name
|
||||||
|
- '@example.com'
|
||||||
|
backends:
|
||||||
|
ldap: mail
|
||||||
|
ad: mail
|
||||||
|
uid:
|
||||||
|
description: "UID of the user"
|
||||||
|
display_name: "UID"
|
||||||
|
search_displayed: True
|
||||||
|
key: True
|
||||||
|
type: string
|
||||||
|
weight: 50
|
||||||
|
autofill:
|
||||||
|
function: lcUid
|
||||||
|
args:
|
||||||
|
- $first-name
|
||||||
|
- $name
|
||||||
|
- '10000'
|
||||||
|
- '40000'
|
||||||
|
backends:
|
||||||
|
ldap: uid
|
||||||
|
ad: sAMAccountName
|
||||||
|
uidNumber:
|
||||||
|
description: "User ID Number of the user"
|
||||||
|
display_name: "UID Number"
|
||||||
|
weight: 60
|
||||||
|
type: int
|
||||||
|
autofill:
|
||||||
|
function: lcUidNumber
|
||||||
|
args:
|
||||||
|
- $first-name
|
||||||
|
- $name
|
||||||
|
- '10000'
|
||||||
|
- '40000'
|
||||||
|
backends:
|
||||||
|
ldap: uidNumber
|
||||||
|
ad: uidNumber
|
||||||
|
gidNumber:
|
||||||
|
description: "Group ID Number of the user"
|
||||||
|
display_name: "GID Number"
|
||||||
|
weight: 70
|
||||||
|
type: int
|
||||||
|
default: '10000'
|
||||||
|
backends:
|
||||||
|
ldap: gidNumber
|
||||||
|
ad: gidNumber
|
||||||
|
shell:
|
||||||
|
description: "Shell of the user"
|
||||||
|
display_name: "Shell"
|
||||||
|
weight: 80
|
||||||
|
self: True
|
||||||
|
type: stringlist
|
||||||
|
values:
|
||||||
|
- /bin/bash
|
||||||
|
- /bin/zsh
|
||||||
|
- /bin/sh
|
||||||
|
backends:
|
||||||
|
ldap: loginShell
|
||||||
|
ad: loginShell
|
||||||
|
home:
|
||||||
|
description: "Home user path"
|
||||||
|
display_name: "Home"
|
||||||
|
weight: 90
|
||||||
|
type: string
|
||||||
|
autofill:
|
||||||
|
function: lcHomeDir
|
||||||
|
args:
|
||||||
|
- $first-name
|
||||||
|
- $name
|
||||||
|
- /home/
|
||||||
|
backends:
|
||||||
|
ldap: homeDirectory
|
||||||
|
ad: homeDirectory
|
||||||
|
password:
|
||||||
|
description: "Password of the user"
|
||||||
|
display_name: "Password"
|
||||||
|
weight: 31
|
||||||
|
self: True
|
||||||
|
type: password
|
||||||
|
backends:
|
||||||
|
ldap: userPassword
|
||||||
|
ad: unicodePwd
|
||||||
|
|
||||||
|
#logscript:
|
||||||
|
# description: "Windows login script"
|
||||||
|
# display_name: "Login script"
|
||||||
|
# weight: 100
|
||||||
|
# type: fix
|
||||||
|
# value: login1.bat
|
||||||
|
# backends:
|
||||||
|
# ad: scriptPath
|
125
tests/test_env/etc/ldapcherry/ldapcherry.ini
Normal file
125
tests/test_env/etc/ldapcherry/ldapcherry.ini
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
# global parameters
|
||||||
|
[global]
|
||||||
|
|
||||||
|
# listing interface
|
||||||
|
server.socket_host = '127.0.0.1'
|
||||||
|
# port
|
||||||
|
server.socket_port = 8080
|
||||||
|
# number of threads
|
||||||
|
server.thread_pool = 8
|
||||||
|
#don't show traceback on error
|
||||||
|
request.show_tracebacks = False
|
||||||
|
|
||||||
|
# log configuration
|
||||||
|
# /!\ you can't have multiple log handlers
|
||||||
|
#####################################
|
||||||
|
# configuration to log in files #
|
||||||
|
#####################################
|
||||||
|
## logger 'file' for access log
|
||||||
|
#log.access_handler = 'file'
|
||||||
|
## logger syslog for error and ldapcherry log
|
||||||
|
#log.error_handler = 'file'
|
||||||
|
## access log file
|
||||||
|
#log.access_file = '/tmp/ldapcherry_access.log'
|
||||||
|
## error and ldapcherry log file
|
||||||
|
#log.error_file = '/tmp/ldapcherry_error.log'
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
# configuration to log in syslog #
|
||||||
|
#####################################
|
||||||
|
# logger syslog for access log
|
||||||
|
#log.access_handler = 'syslog'
|
||||||
|
## logger syslog for error and ldapcherry log
|
||||||
|
log.error_handler = 'syslog'
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
# configuration to not log at all #
|
||||||
|
#####################################
|
||||||
|
# logger none for access log
|
||||||
|
log.access_handler = 'syslog'
|
||||||
|
# logger none for error and ldapcherry log
|
||||||
|
#log.error_handler = 'none'
|
||||||
|
|
||||||
|
# log level
|
||||||
|
log.level = 'debug'
|
||||||
|
|
||||||
|
# session configuration
|
||||||
|
# activate session
|
||||||
|
tools.sessions.on = True
|
||||||
|
# session timeout
|
||||||
|
tools.sessions.timeout = 10
|
||||||
|
# file session storage(to use if multiple processes,
|
||||||
|
# default is in RAM and per process)
|
||||||
|
#tools.sessions.storage_type = "file"
|
||||||
|
# session
|
||||||
|
#tools.sessions.storage_path = "/var/lib/ldapcherry/sessions"
|
||||||
|
|
||||||
|
[attributes]
|
||||||
|
|
||||||
|
# file discribing form content
|
||||||
|
attributes.file = '/etc/ldapcherry/attributes.yml'
|
||||||
|
|
||||||
|
[roles]
|
||||||
|
|
||||||
|
# file listing roles
|
||||||
|
roles.file = '/etc/ldapcherry/roles.yml'
|
||||||
|
|
||||||
|
[backends]
|
||||||
|
|
||||||
|
ldap.module = 'ldapcherry.backend.backendLdap'
|
||||||
|
ldap.groupdn = 'ou=Group,dc=example,dc=org'
|
||||||
|
ldap.userdn = 'ou=people,dc=example,dc=org'
|
||||||
|
ldap.binddn = 'cn=dnscherry,dc=example,dc=org'
|
||||||
|
ldap.password = 'password'
|
||||||
|
ldap.uri = 'ldap://ldap.ldapcherry.org:390'
|
||||||
|
ldap.ca = '/etc/dnscherry/TEST-cacert.pem'
|
||||||
|
ldap.starttls = 'off'
|
||||||
|
ldap.checkcert = 'off'
|
||||||
|
ldap.user_filter_tmpl = '(uid=%(username)s)'
|
||||||
|
ldap.group_filter_tmpl = '(member=%(userdn)s)'
|
||||||
|
ldap.search_filter_tmpl = '(|(uid=%(searchstring)s*)(sn=%(searchstring)s*))'
|
||||||
|
ldap.group_attr.member = "%(dn)s"
|
||||||
|
|
||||||
|
#ldap.objectclasses = 'top, person, organizationalPerson, user'
|
||||||
|
ldap.objectclasses = 'top, person, posixAccount, inetOrgPerson'
|
||||||
|
ldap.dn_user_attr = 'uid'
|
||||||
|
ldap.timeout = 1
|
||||||
|
|
||||||
|
ad.module = 'ldapcherry.backend.backendAD'
|
||||||
|
ad.domain = 'dc.ldapcherry.org'
|
||||||
|
ad.login = 'administrator'
|
||||||
|
ad.password = 'qwertyP455'
|
||||||
|
ad.uri = 'ldaps://ldap.ldapcherry.org:636'
|
||||||
|
ad.checkcert = 'off'
|
||||||
|
|
||||||
|
# authentification parameters
|
||||||
|
[auth]
|
||||||
|
|
||||||
|
# Auth mode
|
||||||
|
# * and: user must authenticate on all backends
|
||||||
|
# * or: user must authenticate on one of the backend
|
||||||
|
# * none: disable authentification
|
||||||
|
# * custom: custom authentification module (need auth.module param)
|
||||||
|
auth.mode = 'none'
|
||||||
|
|
||||||
|
# custom auth module to load
|
||||||
|
#auth.module = 'ldapcherry.auth.modNone'
|
||||||
|
|
||||||
|
[ppolicy]
|
||||||
|
|
||||||
|
# password policy module
|
||||||
|
ppolicy.module = 'ldapcherry.ppolicy.simple'
|
||||||
|
|
||||||
|
# parameters of the module
|
||||||
|
min_length = 2
|
||||||
|
min_upper = 0
|
||||||
|
min_digit = 0
|
||||||
|
|
||||||
|
# resources parameters
|
||||||
|
[resources]
|
||||||
|
# templates directory
|
||||||
|
templates.dir = './resources/templates/'
|
||||||
|
|
||||||
|
[/static]
|
||||||
|
tools.staticdir.on = True
|
||||||
|
tools.staticdir.dir = './resources/static/'
|
36
tests/test_env/etc/ldapcherry/roles.yml
Normal file
36
tests/test_env/etc/ldapcherry/roles.yml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
admin-lv3:
|
||||||
|
display_name: Administrators Level 3
|
||||||
|
description: description
|
||||||
|
backends_groups:
|
||||||
|
ldap:
|
||||||
|
- cn=dns admins,ou=Group,dc=example,dc=org
|
||||||
|
- cn=nagios admins,ou=Group,dc=example,dc=org
|
||||||
|
- cn=puppet admins,ou=Group,dc=example,dc=org
|
||||||
|
- cn=users,ou=Group,dc=example,dc=org
|
||||||
|
ad:
|
||||||
|
- Administrators
|
||||||
|
- Domain Controllers
|
||||||
|
|
||||||
|
admin-lv2:
|
||||||
|
display_name: Administrators Level 2
|
||||||
|
description: description
|
||||||
|
LC_admins: True
|
||||||
|
backends_groups:
|
||||||
|
ldap:
|
||||||
|
- cn=nagios admins,ou=Group,dc=example,dc=org
|
||||||
|
- cn=users,ou=Group,dc=example,dc=org
|
||||||
|
|
||||||
|
developers:
|
||||||
|
display_name: Developpers
|
||||||
|
description: description
|
||||||
|
backends_groups:
|
||||||
|
ldap:
|
||||||
|
- cn=developers,ou=Group,dc=example,dc=org
|
||||||
|
- cn=users,ou=Group,dc=example,dc=org
|
||||||
|
|
||||||
|
users:
|
||||||
|
display_name: Simple Users
|
||||||
|
description: description
|
||||||
|
backends_groups:
|
||||||
|
ldap:
|
||||||
|
- cn=users,ou=Group,dc=example,dc=org
|
Loading…
x
Reference in New Issue
Block a user