├── .github └── workflows │ └── tests.yml ├── .gitignore ├── .travis.yml ├── ChangeLog.rst ├── LICENSE ├── MANIFEST.in ├── README.rst ├── conf ├── attributes.yml ├── ldapcherry.ini └── roles.yml ├── docs ├── Makefile ├── README.rst ├── assets │ ├── banner.css │ ├── basic.css │ ├── cherrypy.jpg │ ├── cherrypy.png │ ├── favicon.png │ ├── logo.png │ ├── nature.css │ ├── pygments.css │ ├── python-logo.png │ ├── python-powered.png │ └── sc │ │ ├── 2015-07-06-093051_1438x1064_scrot.png │ │ ├── 2015-07-06-093130_1438x1064_scrot.png │ │ ├── 2015-07-06-093147_1438x1064_scrot.png │ │ ├── 2015-07-06-093152_1438x1064_scrot.png │ │ ├── 2015-07-06-093215_1438x1064_scrot.png │ │ └── 2015-07-06-093234_1438x1064_scrot.png ├── backend_api.rst ├── backends.rst ├── changelog.rst ├── conf.py ├── deploy.rst ├── docs │ └── assets ├── docs_requirements.txt ├── external_plugins.rst ├── forkme.rst ├── full_configuration.rst ├── goodies.rst ├── index.rst ├── install.rst ├── make.bat ├── ppolicy_api.rst └── screenshots.rst ├── goodies ├── apache.conf ├── demo_backend_configs │ ├── attributes.yml │ ├── ldapcherry.ini │ └── roles.yml ├── gen-dev-conf.sh ├── init-debian ├── lighttpd.conf ├── nginx-fastcgi.conf ├── nginx.conf └── tag.sh ├── ldapcherry ├── __init__.py ├── attributes.py ├── backend │ ├── __init__.py │ ├── backendAD.py │ ├── backendDemo.py │ └── backendLdap.py ├── cli.py ├── exceptions.py ├── lclogging.py ├── ppolicy │ ├── __init__.py │ └── simple.py ├── pyyamlwrapper.py ├── roles.py └── version.py ├── misc ├── debug_attributes.py ├── debug_lc.py ├── debug_ldapbackend.py └── debug_roles.py ├── requirements-el7.txt ├── requirements-stretch.txt ├── requirements.txt ├── resources ├── static │ ├── css │ │ ├── bootstrap-switch.css │ │ ├── bootstrap-theme.css │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap.css │ │ ├── bootstrap.min.css │ │ ├── custom.css │ │ ├── style.css │ │ └── tablesorter-bootstrap.css │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ └── glyphicons-halflings-regular.woff │ ├── img │ │ ├── apple-touch-icon-114-precomposed.png │ │ ├── apple-touch-icon-144-precomposed.png │ │ ├── apple-touch-icon-57-precomposed.png │ │ ├── apple-touch-icon-72-precomposed.png │ │ ├── favicon.png │ │ ├── glyphicons-halflings-white.png │ │ ├── glyphicons-halflings.png │ │ ├── icon.png │ │ └── icon.xcf │ ├── js │ │ ├── alignforms.js │ │ ├── bootstrap-notify.js │ │ ├── bootstrap-switch.js │ │ ├── bootstrap.min.js │ │ ├── html5shiv.js │ │ ├── jquery.min.js │ │ ├── jquery.popconfirm.js │ │ ├── jquery.tablesorter.min.js │ │ ├── lc-filler.js │ │ ├── less-1.3.3.min.js │ │ ├── md5.min.js │ │ ├── ppolicy.js │ │ ├── removediacritic.js │ │ ├── scripts.js │ │ ├── sha1.js │ │ └── validator.js │ └── less │ │ ├── alerts.less │ │ ├── badges.less │ │ ├── bootstrap.less │ │ ├── breadcrumbs.less │ │ ├── button-groups.less │ │ ├── buttons.less │ │ ├── carousel.less │ │ ├── close.less │ │ ├── code.less │ │ ├── component-animations.less │ │ ├── dropdowns.less │ │ ├── forms.less │ │ ├── glyphicons.less │ │ ├── grid.less │ │ ├── input-groups.less │ │ ├── jumbotron.less │ │ ├── labels.less │ │ ├── list-group.less │ │ ├── media.less │ │ ├── mixins.less │ │ ├── modals.less │ │ ├── navbar.less │ │ ├── navs.less │ │ ├── normalize.less │ │ ├── pager.less │ │ ├── pagination.less │ │ ├── panels.less │ │ ├── popovers.less │ │ ├── print.less │ │ ├── progress-bars.less │ │ ├── responsive-utilities.less │ │ ├── scaffolding.less │ │ ├── tables.less │ │ ├── theme.less │ │ ├── thumbnails.less │ │ ├── tooltip.less │ │ ├── type.less │ │ ├── utilities.less │ │ ├── variables.less │ │ └── wells.less └── templates │ ├── 404.tmpl │ ├── adduser.tmpl │ ├── base.tmpl │ ├── error.tmpl │ ├── form.tmpl │ ├── groups.tmpl │ ├── index.tmpl │ ├── login.tmpl │ ├── modify.tmpl │ ├── navbar.tmpl │ ├── roles.tmpl │ ├── searchadmin.tmpl │ ├── searchuser.tmpl │ ├── selfmodify.tmpl │ └── service_unavailable.tmpl ├── run_test.sh ├── setup.py └── tests ├── cfg ├── attribute_pwderror.yml ├── attributes.yml ├── attributes_adldap.yml ├── attributes_missing_mandatory.yml ├── attributes_test.yml ├── attributes_wrong_type.yml ├── blns.json ├── ldapcherry.ini ├── ldapcherry_adldap.cfg ├── ldapcherry_test.ini ├── nested.yml ├── roles.yml ├── roles_adldap.yml ├── roles_content_dup.yml ├── roles_key_dup.yml ├── roles_missing_backends.yml ├── roles_missing_diplay_name.yml ├── roles_test.yml └── wrong_ca.crt ├── disable.py ├── test_Attributes.py ├── test_BackendAD.py ├── test_BackendDemo.py ├── test_BackendLdap.py ├── test_LdapCherry.py ├── test_Roles.py └── test_env ├── deploy.sh └── etc ├── default └── slapd ├── ldap ├── content.ldif ├── slapd.d │ ├── cn=config.ldif │ └── cn=config │ │ ├── cn=module{0}.ldif │ │ ├── cn=schema.ldif │ │ ├── cn=schema │ │ ├── cn={0}core.ldif │ │ ├── cn={1}cosine.ldif │ │ ├── cn={2}nis.ldif │ │ └── cn={3}inetorgperson.ldif │ │ ├── olcDatabase={-1}frontend.ldif │ │ ├── olcDatabase={0}config.ldif │ │ └── olcDatabase={1}mdb.ldif └── ssl │ ├── TEST-cacert.pem │ ├── ldap@dnscherry.org-cert.pem │ └── ldap@dnscherry.org-key.pem └── ldapcherry ├── TEST-cacert.pem ├── attributes.yml ├── ldapcherry.ini ├── roles.yml └── users.db /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build_and_test: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - name: Install packages and setup ldap/AD 16 | run: sudo ./tests/test_env/deploy.sh 17 | 18 | - name: Test 19 | run: python3 setup.py test 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | bin/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | .*.swp 25 | .eggs/ 26 | 27 | # Installer logs 28 | pip-log.txt 29 | pip-delete-this-directory.txt 30 | 31 | # Unit test / coverage reports 32 | htmlcov/ 33 | .tox/ 34 | .coverage 35 | .cache 36 | nosetests.xml 37 | coverage.xml 38 | 39 | # Translations 40 | *.mo 41 | 42 | # Mr Developer 43 | .mr.developer.cfg 44 | .project 45 | .pydevproject 46 | 47 | # Rope 48 | .ropeproject 49 | 50 | # Django stuff: 51 | *.log 52 | *.pot 53 | 54 | # Sphinx documentation 55 | docs/_build/ 56 | 57 | ldapcherry-dev.ini 58 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: xenial 3 | language: python 4 | 5 | before_install: 6 | - '[ "$TEST_PEP8" == "1" ] || sudo ./tests/test_env/deploy.sh' 7 | 8 | install: 9 | - "pip install -e . -r $REQ_FILE" 10 | - "if [[ $TEST_PEP8 == '1' ]]; then pip install pycodestyle; fi" 11 | - pip install passlib 12 | - pip install coveralls 13 | 14 | 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" 15 | matrix: 16 | include: 17 | - python: "2.7" 18 | env: 19 | TEST_PEP8=1 20 | REQ_FILE=requirements.txt 21 | - python: "2.7" 22 | env: 23 | TEST_PEP8=0 24 | REQ_FILE=requirements-el7.txt 25 | - python: "2.7" 26 | env: 27 | TEST_PEP8=0 28 | REQ_FILE=requirements-stretch.txt 29 | - python: "2.7" 30 | env: 31 | TEST_PEP8=0 32 | REQ_FILE=requirements.txt 33 | - python: "3.6" 34 | env: 35 | TEST_PEP8=0 36 | REQ_FILE=requirements.txt 37 | 38 | after_success: 39 | - coveralls 40 | after_failure: 41 | - sudo cat /var/log/syslog 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Carpentier Pierre-Francois 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.rst 2 | graft conf 3 | graft docs 4 | graft goodies 5 | graft resources 6 | graft tests 7 | include LICENSE 8 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ************** 2 | LdapCherry 3 | ************** 4 | 5 | .. image:: https://raw.githubusercontent.com/kakwa/ldapcherry/master/resources/static/img/apple-touch-icon-72-precomposed.png 6 | 7 | Nice and simple application to manage users and groups in multiple directory services. 8 | 9 | .. image:: https://github.com/kakwa/ldapcherry/actions/workflows/tests.yml/badge.svg 10 | :target: https://github.com/kakwa/ldapcherry/actions/workflows/tests.yml 11 | :alt: CI 12 | 13 | .. image:: https://img.shields.io/pypi/v/ldapcherry.svg 14 | :target: https://pypi.python.org/pypi/ldapcherry 15 | :alt: PyPI version 16 | 17 | .. image:: https://readthedocs.org/projects/ldapcherry/badge/?version=latest 18 | :target: http://ldapcherry.readthedocs.org/en/latest/?badge=latest 19 | :alt: Documentation Status 20 | 21 | ---- 22 | 23 | :Doc: `LdapCherry documentation on ReadTheDoc `_ 24 | :Dev: `LdapCherry source code on GitHub `_ 25 | :PyPI: `LdapCherry package on Pypi `_ 26 | :License: MIT 27 | :Author: Pierre-Francois Carpentier - copyright © 2016 28 | 29 | ---- 30 | 31 | **************** 32 | Presentation 33 | **************** 34 | 35 | LdapCherry is a CherryPY application to manage users and groups in multiple directory services. 36 | 37 | Its main features are: 38 | 39 | * manage multiple directories/databases backends in an unified way 40 | * roles management (as in "groups of groups") 41 | * autofill forms 42 | * password policy 43 | * self modification of some selected fields by normal (non administrator) users 44 | * nice bootstrap interface 45 | * modular through pluggable authentication, password policy and backend modules 46 | 47 | LdapCherry is not limited to ldap, it can handle virtually any user backend (ex: SQL database, htpasswd file, etc) 48 | through the proper plugin (provided that it is implemented ^^). 49 | 50 | LdapCherry also aims to be as simple as possible to deploy: no crazy dependencies, 51 | few configuration files, extensive debug logs and full documentation. 52 | 53 | The default backend plugins permit to manage Ldap and Active Directory. 54 | 55 | *************** 56 | Screenshots 57 | *************** 58 | 59 | `Screenshots `_. 60 | 61 | *********** 62 | Try out 63 | *********** 64 | 65 | .. sourcecode:: bash 66 | 67 | # clone the repository 68 | $ git clone https://github.com/kakwa/ldapcherry && cd ldapcherry 69 | 70 | # change the directory where to put the configuration (default: /etc) 71 | $ export SYSCONFDIR=/etc 72 | # change the directory where to put the resource (default: /usr/share) 73 | $ export DATAROOTDIR=/usr/share/ 74 | 75 | # install ldapcherry 76 | $ python setup.py install 77 | 78 | # edit configuration files 79 | $ vi /etc/ldapcherry/ldapcherry.ini 80 | $ vi /etc/ldapcherry/roles.yml 81 | $ vi /etc/ldapcherry/attributes.yml 82 | 83 | # launch ldapcherry 84 | $ ldapcherryd -c /etc/ldapcherry/ldapcherry.ini -D 85 | 86 | 87 | Debian and RPM packages are also available here: `https://github.com/kakwa/kakwalab-pkg` (package name ``ldapcherry``). 88 | 89 | *********** 90 | License 91 | *********** 92 | 93 | LdapCherry is published under the MIT Public License. 94 | 95 | ******************************* 96 | Discussion / Help / Updates 97 | ******************************* 98 | 99 | * IRC: `Libera `_ ``#ldapcherry`` channel 100 | * Bugtracker: `Github `_ 101 | 102 | ---- 103 | 104 | .. image:: https://raw.githubusercontent.com/kakwa/ldapcherry/master/docs/assets/python-powered.png 105 | .. image:: https://raw.githubusercontent.com/kakwa/ldapcherry/master/docs/assets/cherrypy.png 106 | -------------------------------------------------------------------------------- /conf/attributes.yml: -------------------------------------------------------------------------------- 1 | cn: 2 | description: "First Name and Display Name" 3 | display_name: "Display Name" 4 | type: string 5 | weight: 30 6 | autofill: 7 | function: lcDisplayName 8 | args: 9 | - $first-name 10 | - $name 11 | backends: 12 | ldap: cn 13 | # ad: cn 14 | first-name: 15 | description: "First name of the user" 16 | display_name: "First Name" 17 | search_displayed: True 18 | type: string 19 | weight: 20 20 | backends: 21 | ldap: givenName 22 | # ad: givenName 23 | name: 24 | description: "Family name of the user" 25 | display_name: "Name" 26 | search_displayed: True 27 | weight: 10 28 | type: string 29 | backends: 30 | ldap: sn 31 | # ad: sn 32 | email: 33 | description: "Email of the user" 34 | display_name: "Email" 35 | search_displayed: True 36 | type: email 37 | weight: 40 38 | autofill: 39 | function: lcMail 40 | args: 41 | - $first-name 42 | - $name 43 | - '@example.com' 44 | backends: 45 | ldap: mail 46 | # ad: mail 47 | uid: 48 | description: "UID of the user" 49 | display_name: "UID" 50 | search_displayed: True 51 | key: True 52 | type: string 53 | weight: 50 54 | autofill: 55 | function: lcUid 56 | args: 57 | - $first-name 58 | - $name 59 | - '10000' 60 | - '40000' 61 | backends: 62 | ldap: uid 63 | # ad: sAMAccountName 64 | uidNumber: 65 | description: "User ID Number of the user" 66 | display_name: "UID Number" 67 | weight: 60 68 | type: int 69 | autofill: 70 | function: lcUidNumber 71 | args: 72 | - $first-name 73 | - $name 74 | - '10000' 75 | - '40000' 76 | backends: 77 | ldap: uidNumber 78 | # ad: uidNumber 79 | gidNumber: 80 | description: "Group ID Number of the user" 81 | display_name: "GID Number" 82 | weight: 70 83 | type: int 84 | default: '10000' 85 | backends: 86 | ldap: gidNumber 87 | # ad: gidNumber 88 | shell: 89 | description: "Shell of the user" 90 | display_name: "Shell" 91 | weight: 80 92 | self: True 93 | type: stringlist 94 | values: 95 | - /bin/bash 96 | - /bin/zsh 97 | - /bin/sh 98 | backends: 99 | ldap: loginShell 100 | # ad: loginShell 101 | home: 102 | description: "Home user path" 103 | display_name: "Home" 104 | weight: 90 105 | type: string 106 | autofill: 107 | function: lcHomeDir 108 | args: 109 | - $first-name 110 | - $name 111 | - /home/ 112 | backends: 113 | ldap: homeDirectory 114 | # ad: homeDirectory 115 | password: 116 | description: "Password of the user" 117 | display_name: "Password" 118 | weight: 31 119 | self: True 120 | type: password 121 | backends: 122 | ldap: userPassword 123 | # ad: unicodePwd 124 | 125 | #logscript: 126 | # description: "Windows login script" 127 | # display_name: "Login script" 128 | # weight: 100 129 | # type: fix 130 | # value: login1.bat 131 | # backends: 132 | # ad: scriptPath 133 | -------------------------------------------------------------------------------- /conf/roles.yml: -------------------------------------------------------------------------------- 1 | admin-lv3: 2 | display_name: Administrators Level 3 3 | description: Super administrators of the system 4 | backends_groups: 5 | ldap: 6 | - cn=dns admins,ou=Group,dc=example,dc=org 7 | - cn=nagios admins,ou=Group,dc=example,dc=org 8 | - cn=puppet admins,ou=Group,dc=example,dc=org 9 | - cn=users,ou=Group,dc=example,dc=org 10 | # ad: 11 | # - Administrators 12 | # - Group Policy Creator Owners 13 | # - Enterprise Admins 14 | # - Schema Admins 15 | # - Domain Admins 16 | 17 | admin-lv2: 18 | display_name: Administrators Level 2 19 | description: Basic administrators of the system 20 | LC_admins: True 21 | backends_groups: 22 | ldap: 23 | - cn=nagios admins,ou=Group,dc=example,dc=org 24 | - cn=users,ou=Group,dc=example,dc=org 25 | # ad: 26 | # - Administrators 27 | 28 | developers: 29 | display_name: Developpers 30 | description: Developpers of the system 31 | backends_groups: 32 | ldap: 33 | - cn=developers,ou=Group,dc=example,dc=org 34 | - cn=users,ou=Group,dc=example,dc=org 35 | 36 | users: 37 | display_name: Simple Users 38 | description: Basic users of the system 39 | backends_groups: 40 | ldap: 41 | - cn=users,ou=Group,dc=example,dc=org 42 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = -a 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | 15 | .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest 16 | 17 | help: 18 | @echo "Please use \`make ' where is one of" 19 | @echo " html to make standalone HTML files" 20 | @echo " dirhtml to make HTML files named index.html in directories" 21 | @echo " pickle to make pickle files" 22 | @echo " json to make JSON files" 23 | @echo " htmlhelp to make HTML files and a HTML help project" 24 | @echo " qthelp to make HTML files and a qthelp project" 25 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 26 | @echo " changes to make an overview of all changed/added/deprecated items" 27 | @echo " linkcheck to check all external links for integrity" 28 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 29 | 30 | clean: 31 | -rm -rf $(BUILDDIR)/* 32 | 33 | html: 34 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 35 | @echo 36 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 37 | 38 | dirhtml: 39 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 40 | @echo 41 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 42 | 43 | pickle: 44 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 45 | @echo 46 | @echo "Build finished; now you can process the pickle files." 47 | 48 | json: 49 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 50 | @echo 51 | @echo "Build finished; now you can process the JSON files." 52 | 53 | htmlhelp: 54 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 55 | @echo 56 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 57 | ".hhp project file in $(BUILDDIR)/htmlhelp." 58 | 59 | qthelp: 60 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 61 | @echo 62 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 63 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 64 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-cumulus.qhcp" 65 | @echo "To view the help file:" 66 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-cumulus.qhc" 67 | 68 | latex: 69 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 70 | @echo 71 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 72 | @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ 73 | "run these through (pdf)latex." 74 | 75 | changes: 76 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 77 | @echo 78 | @echo "The overview file is in $(BUILDDIR)/changes." 79 | 80 | linkcheck: 81 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 82 | @echo 83 | @echo "Link check complete; look for any errors in the above output " \ 84 | "or in $(BUILDDIR)/linkcheck/output.txt." 85 | 86 | doctest: 87 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 88 | @echo "Testing of doctests in the sources finished, look at the " \ 89 | "results in $(BUILDDIR)/doctest/output.txt." 90 | -------------------------------------------------------------------------------- /docs/README.rst: -------------------------------------------------------------------------------- 1 | --------------------------------------- 2 | Build the LdapCherry docs/website: 3 | --------------------------------------- 4 | 5 | The Ldapcherry documentation/website is built using `Sphinx`_. 6 | 7 | .. _Sphinx: http://sphinx.pocoo.org/ 8 | 9 | To build the docs you need to perform the following tasks: 10 | 11 | * Install Sphinx (``$ sudo apt-get install python-sphinx``) 12 | * From this ``docs`` directory, run: ``$ make html`` 13 | * This will produce HTML documentation in the ``_build/html/`` directory 14 | * Open ``_build/html/index.html`` with your browser 15 | -------------------------------------------------------------------------------- /docs/assets/cherrypy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/docs/assets/cherrypy.jpg -------------------------------------------------------------------------------- /docs/assets/cherrypy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/docs/assets/cherrypy.png -------------------------------------------------------------------------------- /docs/assets/favicon.png: -------------------------------------------------------------------------------- 1 | ../../resources/static/img/favicon.png -------------------------------------------------------------------------------- /docs/assets/logo.png: -------------------------------------------------------------------------------- 1 | ../../resources/static/img/apple-touch-icon-57-precomposed.png -------------------------------------------------------------------------------- /docs/assets/pygments.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight { background: #eeffcc; } 3 | .highlight .c { color: #408090; font-style: italic } /* Comment */ 4 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 5 | .highlight .k { color: #007020; font-weight: bold } /* Keyword */ 6 | .highlight .o { color: #666666 } /* Operator */ 7 | .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ 8 | .highlight .cp { color: #007020 } /* Comment.Preproc */ 9 | .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ 10 | .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ 11 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 12 | .highlight .ge { font-style: italic } /* Generic.Emph */ 13 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 14 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 15 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 16 | .highlight .go { color: #333333 } /* Generic.Output */ 17 | .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ 18 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 19 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 20 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 21 | .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ 22 | .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ 23 | .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ 24 | .highlight .kp { color: #007020 } /* Keyword.Pseudo */ 25 | .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ 26 | .highlight .kt { color: #902000 } /* Keyword.Type */ 27 | .highlight .m { color: #208050 } /* Literal.Number */ 28 | .highlight .s { color: #990066 } /* Literal.String */ 29 | .highlight .na { color: #009966 } /* Name.Attribute */ 30 | .highlight .nb { color: #007020 } /* Name.Builtin */ 31 | .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ 32 | .highlight .no { color: #60add5 } /* Name.Constant */ 33 | .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ 34 | .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ 35 | .highlight .ne { color: #007020 } /* Name.Exception */ 36 | .highlight .nf { color: #06287e } /* Name.Function */ 37 | .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ 38 | .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ 39 | .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ 40 | .highlight .nv { color: #bb60d5 } /* Name.Variable */ 41 | .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ 42 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 43 | .highlight .mf { color: #208050 } /* Literal.Number.Float */ 44 | .highlight .mh { color: #208050 } /* Literal.Number.Hex */ 45 | .highlight .mi { color: #208050 } /* Literal.Number.Integer */ 46 | .highlight .mo { color: #208050 } /* Literal.Number.Oct */ 47 | .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ 48 | .highlight .sc { color: #4070a0 } /* Literal.String.Char */ 49 | .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ 50 | .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ 51 | .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ 52 | .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ 53 | .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ 54 | .highlight .sx { color: #c65d09 } /* Literal.String.Other */ 55 | .highlight .sr { color: #235388 } /* Literal.String.Regex */ 56 | .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ 57 | .highlight .ss { color: #517918 } /* Literal.String.Symbol */ 58 | .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ 59 | .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ 60 | .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ 61 | .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ 62 | .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ 63 | -------------------------------------------------------------------------------- /docs/assets/python-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/docs/assets/python-logo.png -------------------------------------------------------------------------------- /docs/assets/python-powered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/docs/assets/python-powered.png -------------------------------------------------------------------------------- /docs/assets/sc/2015-07-06-093051_1438x1064_scrot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/docs/assets/sc/2015-07-06-093051_1438x1064_scrot.png -------------------------------------------------------------------------------- /docs/assets/sc/2015-07-06-093130_1438x1064_scrot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/docs/assets/sc/2015-07-06-093130_1438x1064_scrot.png -------------------------------------------------------------------------------- /docs/assets/sc/2015-07-06-093147_1438x1064_scrot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/docs/assets/sc/2015-07-06-093147_1438x1064_scrot.png -------------------------------------------------------------------------------- /docs/assets/sc/2015-07-06-093152_1438x1064_scrot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/docs/assets/sc/2015-07-06-093152_1438x1064_scrot.png -------------------------------------------------------------------------------- /docs/assets/sc/2015-07-06-093215_1438x1064_scrot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/docs/assets/sc/2015-07-06-093215_1438x1064_scrot.png -------------------------------------------------------------------------------- /docs/assets/sc/2015-07-06-093234_1438x1064_scrot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/docs/assets/sc/2015-07-06-093234_1438x1064_scrot.png -------------------------------------------------------------------------------- /docs/backend_api.rst: -------------------------------------------------------------------------------- 1 | Implementing cutom backends 2 | =========================== 3 | 4 | API 5 | --- 6 | 7 | The backend modules must respect the following API: 8 | 9 | .. autoclass:: ldapcherry.backend.Backend 10 | :members: __init__, auth, add_user, del_user, set_attrs, add_to_groups, del_from_groups, search, get_user, get_groups 11 | :undoc-members: 12 | :show-inheritance: 13 | 14 | Configuration 15 | ------------- 16 | 17 | Configuration for your backend is declared in the main ini file, inside [backends] section: 18 | 19 | For example with the configuration: 20 | 21 | .. sourcecode:: ini 22 | 23 | [backends] 24 | 25 | # class path to module 26 | b_id.module = "my.backend.module" 27 | 28 | b_id.param1 = "my value 1" 29 | b_id.param2 = "my value 2" 30 | 31 | .. note:: 32 | 33 | One module can be instanciated several times, the prefix b_id permits 34 | to differenciate instances and their specific configuration. 35 | 36 | The following hash will be passed as configuration to the module constructor as parameter config: 37 | 38 | .. sourcecode:: python 39 | 40 | { 41 | 'param1': "my value 1", 42 | 'param2': "my value 2", 43 | } 44 | 45 | After having set **self.config** to **config** in the constructor, parameters can be recovered 46 | by **self.get_param**: 47 | 48 | .. autoclass:: ldapcherry.backend.Backend 49 | :members: get_param 50 | :undoc-members: 51 | :show-inheritance: 52 | 53 | 54 | Exceptions 55 | ---------- 56 | 57 | The following exception can be used in your module 58 | 59 | .. automodule:: ldapcherry.exceptions 60 | :members: UserDoesntExist, UserAlreadyExists, GroupDoesntExist 61 | :undoc-members: 62 | :show-inheritance: 63 | 64 | These exceptions permit a nicer error handling and avoid a generic message to be thrown at the user. 65 | 66 | Example 67 | ------- 68 | 69 | Here is the ldap backend module that comes with LdapCherry: 70 | 71 | .. literalinclude:: ../ldapcherry/backend/backendDemo.py 72 | :language: python 73 | -------------------------------------------------------------------------------- /docs/changelog.rst: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | .. include:: ../ChangeLog.rst 5 | 6 | -------------------------------------------------------------------------------- /docs/docs/assets: -------------------------------------------------------------------------------- 1 | ../assets -------------------------------------------------------------------------------- /docs/docs_requirements.txt: -------------------------------------------------------------------------------- 1 | CherryPy>=3.0.0 2 | PyYAML 3 | Mako 4 | mock 5 | -------------------------------------------------------------------------------- /docs/external_plugins.rst: -------------------------------------------------------------------------------- 1 | LdapCherry plugins list 2 | ======================= 3 | 4 | If you have developped OSS LdapCherry plugins, please fill an `Issue Here `_ 5 | with a link to your plugin and a small description. 6 | 7 | Password Policy plugins 8 | ----------------------- 9 | 10 | * `Cracklib PPolicy `_ 11 | 12 | Backend plugins 13 | --------------- 14 | 15 | -------------------------------------------------------------------------------- /docs/forkme.rst: -------------------------------------------------------------------------------- 1 | .. raw:: html 2 | 3 | Fork me on GitHub 4 | -------------------------------------------------------------------------------- /docs/full_configuration.rst: -------------------------------------------------------------------------------- 1 | Full Configuration 2 | ================== 3 | 4 | Main ini configuration file 5 | --------------------------- 6 | 7 | .. literalinclude:: ../conf/ldapcherry.ini 8 | :language: ini 9 | 10 | 11 | Yaml Attributes configuration file 12 | ---------------------------------- 13 | 14 | .. literalinclude:: ../conf/attributes.yml 15 | :language: yaml 16 | 17 | 18 | Yaml Roles configuration file 19 | ----------------------------- 20 | 21 | .. literalinclude:: ../conf/roles.yml 22 | :language: yaml 23 | 24 | -------------------------------------------------------------------------------- /docs/goodies.rst: -------------------------------------------------------------------------------- 1 | Some Goodies 2 | ============ 3 | 4 | Here are some goodies that might help deploying LdapCherry 5 | 6 | They are located in the **goodies/** directory. 7 | 8 | Init Script 9 | ----------- 10 | 11 | Sample init script for Debian: 12 | 13 | .. literalinclude:: ../goodies/init-debian 14 | :language: bash 15 | 16 | This init script is available in **goodies/init-debian**. 17 | 18 | Apache Vhost 19 | ------------ 20 | 21 | Basic Apache Vhost: 22 | 23 | .. literalinclude:: ../goodies/apache.conf 24 | :language: xml 25 | 26 | Nginx Vhost 27 | ----------- 28 | 29 | Basic Nginx Vhost: 30 | 31 | .. literalinclude:: ../goodies/nginx.conf 32 | :language: yaml 33 | 34 | Nginx Vhost (FastCGI) 35 | --------------------- 36 | 37 | Nginx Vhost in FastCGI mode: 38 | 39 | .. literalinclude:: ../goodies/nginx-fastcgi.conf 40 | :language: yaml 41 | 42 | .. warning:: 43 | 44 | LdapCherry requires the python flup module to run in FastCGI 45 | 46 | Lighttpd Vhost 47 | -------------- 48 | 49 | Basic Lighttpd Vhost 50 | 51 | .. literalinclude:: ../goodies/lighttpd.conf 52 | :language: yaml 53 | 54 | Demo Backend Configuration Files 55 | -------------------------------- 56 | 57 | The files here are the ones that are used at the demo site at `ldapcherry.kakwalab.ovh `_ and can be used for a self-hosted demo backend: 58 | 59 | .. literalinclude:: ../goodies/demo_backend_configs/attributes.yml 60 | :language: yaml 61 | 62 | .. literalinclude:: ../goodies/demo_backend_configs/roles.yml 63 | :language: yaml 64 | 65 | .. literalinclude:: ../goodies/demo_backend_configs/ldapcherry.ini 66 | :language: ini 67 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | ******************** 2 | Table of Content 3 | ******************** 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | install 9 | deploy 10 | backends 11 | full_configuration 12 | external_plugins 13 | backend_api 14 | ppolicy_api 15 | changelog 16 | goodies 17 | screenshots 18 | 19 | .. include:: ../README.rst 20 | 21 | .. include:: forkme.rst 22 | -------------------------------------------------------------------------------- /docs/install.rst: -------------------------------------------------------------------------------- 1 | Install 2 | ======= 3 | 4 | From the sources 5 | ---------------- 6 | 7 | Download the latest release from `GitHub `_. 8 | 9 | .. sourcecode:: bash 10 | 11 | $ tar -xf ldapcherry*.tar.gz 12 | $ cd ldapcherry* 13 | $ python setup.py install 14 | 15 | Alternatively, you can install from git: 16 | 17 | .. sourcecode:: bash 18 | 19 | $ git clone https://github.com/kakwa/ldapcherry 20 | $ cd ldapcherry 21 | $ python setup.py install 22 | 23 | Installed files 24 | --------------- 25 | 26 | ldapCherry install directories are: 27 | 28 | * **/etc/ldapcherry/** (configuration) 29 | * **dist-package** or **site-packages** of your distribution (LdapCherry modules) 30 | * **/usr/share/ldapcherry/** (static content (css, js, images...) and templates) 31 | 32 | These directories can be changed by exporting the following variables before launching the install command: 33 | 34 | .. sourcecode:: bash 35 | 36 | # optional, default sys.prefix + 'share' (/usr/share/ on most Linux) 37 | $ export DATAROOTDIR=/usr/local/share/ 38 | 39 | # optional, default /etc/ 40 | $ export SYSCONFDIR=/usr/local/etc/ 41 | 42 | .. note:: if --root is passed, the install prefix is honored for these directories 43 | 44 | .. warning:: If you change these directories, **templates.dir** and **tools.staticdir.dir** in *ldapcherry.ini* need to be modified accordingly. 45 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | set SPHINXBUILD=sphinx-build 6 | set BUILDDIR=_build 7 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 8 | if NOT "%PAPER%" == "" ( 9 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 10 | ) 11 | 12 | if "%1" == "" goto help 13 | 14 | if "%1" == "help" ( 15 | :help 16 | echo.Please use `make ^` where ^ is one of 17 | echo. html to make standalone HTML files 18 | echo. dirhtml to make HTML files named index.html in directories 19 | echo. pickle to make pickle files 20 | echo. json to make JSON files 21 | echo. htmlhelp to make HTML files and a HTML help project 22 | echo. qthelp to make HTML files and a qthelp project 23 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 24 | echo. changes to make an overview over all changed/added/deprecated items 25 | echo. linkcheck to check all external links for integrity 26 | echo. doctest to run all doctests embedded in the documentation if enabled 27 | goto end 28 | ) 29 | 30 | if "%1" == "clean" ( 31 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 32 | del /q /s %BUILDDIR%\* 33 | goto end 34 | ) 35 | 36 | if "%1" == "html" ( 37 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 38 | echo. 39 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 40 | goto end 41 | ) 42 | 43 | if "%1" == "dirhtml" ( 44 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 45 | echo. 46 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 47 | goto end 48 | ) 49 | 50 | if "%1" == "pickle" ( 51 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 52 | echo. 53 | echo.Build finished; now you can process the pickle files. 54 | goto end 55 | ) 56 | 57 | if "%1" == "json" ( 58 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 59 | echo. 60 | echo.Build finished; now you can process the JSON files. 61 | goto end 62 | ) 63 | 64 | if "%1" == "htmlhelp" ( 65 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 66 | echo. 67 | echo.Build finished; now you can run HTML Help Workshop with the ^ 68 | .hhp project file in %BUILDDIR%/htmlhelp. 69 | goto end 70 | ) 71 | 72 | if "%1" == "qthelp" ( 73 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 74 | echo. 75 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 76 | .qhcp project file in %BUILDDIR%/qthelp, like this: 77 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\django-cumulus.qhcp 78 | echo.To view the help file: 79 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\django-cumulus.ghc 80 | goto end 81 | ) 82 | 83 | if "%1" == "latex" ( 84 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 85 | echo. 86 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 87 | goto end 88 | ) 89 | 90 | if "%1" == "changes" ( 91 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 92 | echo. 93 | echo.The overview file is in %BUILDDIR%/changes. 94 | goto end 95 | ) 96 | 97 | if "%1" == "linkcheck" ( 98 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 99 | echo. 100 | echo.Link check complete; look for any errors in the above output ^ 101 | or in %BUILDDIR%/linkcheck/output.txt. 102 | goto end 103 | ) 104 | 105 | if "%1" == "doctest" ( 106 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 107 | echo. 108 | echo.Testing of doctests in the sources finished, look at the ^ 109 | results in %BUILDDIR%/doctest/output.txt. 110 | goto end 111 | ) 112 | 113 | :end 114 | -------------------------------------------------------------------------------- /docs/ppolicy_api.rst: -------------------------------------------------------------------------------- 1 | Implementing password policy modules 2 | ==================================== 3 | 4 | API 5 | --- 6 | 7 | The password policy modules must respect following API: 8 | 9 | .. autoclass:: ldapcherry.ppolicy.PPolicy 10 | :members: check, info, __init__ 11 | :undoc-members: 12 | :show-inheritance: 13 | 14 | Configuration 15 | ------------- 16 | 17 | Parameters are declared in the main configuration file, inside the **ppolicy** section. 18 | 19 | After having set **self.config** to **config** in the constructor, parameters can be recovered 20 | by **self.get_param**: 21 | 22 | .. autoclass:: ldapcherry.ppolicy.PPolicy 23 | :members: get_param 24 | :undoc-members: check 25 | :show-inheritance: 26 | 27 | Example 28 | ------- 29 | 30 | Here is the simple default ppolicy module that comes with LdapCherry: 31 | 32 | .. literalinclude:: ../ldapcherry/ppolicy/simple.py 33 | :language: python 34 | 35 | -------------------------------------------------------------------------------- /docs/screenshots.rst: -------------------------------------------------------------------------------- 1 | Screenshots 2 | =========== 3 | 4 | .. raw:: html 5 | 6 | 54 |
55 | 56 | 57 | 58 | 59 | 60 | 61 |
62 |
63 | 1 64 | 2 65 | 3 66 | 4 67 | 5 68 | 6 69 |
70 | -------------------------------------------------------------------------------- /goodies/apache.conf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ProxyPass http://127.0.0.1:8080/ 5 | ProxyPassReverse http://127.0.0.1:8080/ 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /goodies/demo_backend_configs/attributes.yml: -------------------------------------------------------------------------------- 1 | description: "First Name and Display Name" 2 | display_name: "Display Name" 3 | type: string 4 | weight: 30 5 | autofill: 6 | function: lcDisplayName 7 | args: 8 | - $first-name 9 | - $name 10 | backends: 11 | demo: cn 12 | first-name: 13 | description: "First name of the user" 14 | display_name: "First Name" 15 | search_displayed: True 16 | type: string 17 | weight: 20 18 | backends: 19 | demo: givenName 20 | name: 21 | description: "Family name of the user" 22 | display_name: "Name" 23 | search_displayed: True 24 | weight: 10 25 | type: string 26 | backends: 27 | demo: sn 28 | email: 29 | description: "Email of the user" 30 | display_name: "Email" 31 | search_displayed: True 32 | type: email 33 | weight: 40 34 | autofill: 35 | function: lcMail 36 | args: 37 | - $first-name 38 | - $name 39 | - '@example.com' 40 | backends: 41 | demo: mail 42 | uid: 43 | description: "UID of the user" 44 | display_name: "UID" 45 | search_displayed: True 46 | key: True 47 | type: string 48 | weight: 50 49 | autofill: 50 | function: lcUid 51 | args: 52 | - $first-name 53 | - $name 54 | - '10000' 55 | - '40000' 56 | backends: 57 | demo: uid 58 | uidNumber: 59 | description: "User ID Number of the user" 60 | display_name: "UID Number" 61 | weight: 60 62 | type: int 63 | autofill: 64 | function: lcUidNumber 65 | args: 66 | - $first-name 67 | - $name 68 | - '10000' 69 | - '40000' 70 | backends: 71 | demo: uidNumber 72 | gidNumber: 73 | description: "Group ID Number of the user" 74 | display_name: "GID Number" 75 | weight: 70 76 | type: int 77 | default: '10000' 78 | backends: 79 | demo: gidNumber 80 | shell: 81 | description: "Shell of the user" 82 | display_name: "Shell" 83 | weight: 80 84 | self: True 85 | type: stringlist 86 | values: 87 | - /bin/bash 88 | - /bin/zsh 89 | - /bin/sh 90 | backends: 91 | demo: loginShell 92 | home: 93 | description: "Home user path" 94 | display_name: "Home" 95 | weight: 90 96 | type: string 97 | autofill: 98 | function: lcHomeDir 99 | args: 100 | - $first-name 101 | - $name 102 | - /home/ 103 | backends: 104 | demo: homeDirectory 105 | password: 106 | description: "Password of the user" 107 | display_name: "Password" 108 | weight: 31 109 | self: True 110 | type: password 111 | backends: 112 | demo: userPassword 113 | -------------------------------------------------------------------------------- /goodies/demo_backend_configs/ldapcherry.ini: -------------------------------------------------------------------------------- 1 | [backends] 2 | ##################################### 3 | # configuration of demo backend # 4 | ##################################### 5 | 6 | # Name of the backend 7 | demo.module = 'ldapcherry.backend.backendDemo' 8 | # Display name of the Backend 9 | demo.display_name = 'Demo Backend' 10 | # Groups of admin user 11 | demo.admin.groups = 'SECOFF' 12 | # Groups of basic user 13 | demo.basic.groups = 'Test 2, Test 1' 14 | # Password attribute name 15 | demo.pwd_attr = 'userPassword' 16 | # Attribute to use for the search 17 | demo.search_attributes = 'cn, sn, givenName, uid' 18 | # Login of default admin user 19 | demo.admin.user = 'admin' 20 | # Password of default admin user 21 | demo.admin.password = 'admin' 22 | # Login of default basic user 23 | demo.basic.user = 'user' 24 | # Password of default basic user 25 | demo.basic.password = 'user' 26 | -------------------------------------------------------------------------------- /goodies/demo_backend_configs/roles.yml: -------------------------------------------------------------------------------- 1 | sec-officer: 2 | display_name: Security Officer 3 | description: Security officer of the system 4 | LC_admins: True 5 | backends_groups: 6 | demo: 7 | - SECOFF 8 | admin-lv3: 9 | display_name: Administrators Level 3 10 | description: Super administrators of the system 11 | backends_groups: 12 | demo: 13 | - cn=dns admins,ou=Group,dc=example,dc=org 14 | - cn=nagios admins,ou=Group,dc=example,dc=org 15 | - cn=puppet admins,ou=Group,dc=example,dc=org 16 | - cn=users,ou=Group,dc=example,dc=org 17 | admin-lv2: 18 | display_name: Administrators Level 2 19 | description: Basic administrators of the system 20 | backends_groups: 21 | demo: 22 | - cn=nagios admins,ou=Group,dc=example,dc=org 23 | - cn=users,ou=Group,dc=example,dc=org 24 | developpers: 25 | display_name: Developpers 26 | description: Developpers of the system 27 | backends_groups: 28 | demo: 29 | - cn=developpers,ou=Group,dc=example,dc=org 30 | - cn=users,ou=Group,dc=example,dc=org 31 | users: 32 | display_name: Simple Users 33 | description: Basic users of the system 34 | backends_groups: 35 | demo: 36 | - cn=users,ou=Group,dc=example,dc=org 37 | -------------------------------------------------------------------------------- /goodies/gen-dev-conf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ROOT=$(readlink -f $(dirname $0)/../) 4 | 5 | cp $ROOT/conf/ldapcherry.ini $ROOT/ldapcherry-dev.ini 6 | 7 | sed -i "s|/etc/ldapcherry/|$ROOT/conf/|" $ROOT/ldapcherry-dev.ini 8 | sed -i "s|/usr/share/ldapcherry/|$ROOT/resources/|" $ROOT/ldapcherry-dev.ini 9 | sed -i "s|^ldap\.|#ldap.|" $ROOT/ldapcherry-dev.ini 10 | sed -i "s|#demo\.|ldap.|" $ROOT/ldapcherry-dev.ini 11 | 12 | GROUPS='cn=nagios admins\\,ou=Group\\,dc=example\\,dc=org, cn=users\\,ou=Group\\,dc=example\\,dc=org' 13 | sed -i "s|ldap.admin.groups.*|ldap.admin.groups = '$GROUPS'|" $ROOT/ldapcherry-dev.ini 14 | 15 | 16 | sed -i "s|^min_length.*|min_length = 3|" $ROOT/ldapcherry-dev.ini 17 | sed -i "s|^min_upper.*|min_upper = 0|" $ROOT/ldapcherry-dev.ini 18 | sed -i "s|^min_digit.*|min_digit = 0|" $ROOT/ldapcherry-dev.ini 19 | -------------------------------------------------------------------------------- /goodies/init-debian: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | ### BEGIN INIT INFO 4 | # Provides: ldapcherryd 5 | # Required-Start: $remote_fs $network $syslog 6 | # Required-Stop: $remote_fs $network $syslog 7 | # Default-Start: 2 3 4 5 8 | # Default-Stop: 9 | # Short-Description: ldapcherry 10 | ### END INIT INFO 11 | 12 | PIDFILE=/var/run/ldapcherryd/ldapcherryd.pid 13 | CONF=/etc/ldapcherry/ldapcherry.ini 14 | USER=www-data 15 | GROUP=www-data 16 | BIN=/usr/local/bin/ldapcherryd 17 | OPTS="-d -c $CONF -p $PIDFILE" 18 | 19 | . /lib/lsb/init-functions 20 | 21 | if [ -f /etc/default/ldapcherryd ]; then 22 | . /etc/default/ldapcherryd 23 | fi 24 | 25 | start_ldapcherryd(){ 26 | log_daemon_msg "Starting ldapcherryd" "ldapcherryd" || true 27 | pidofproc -p $PIDFILE $BIN >/dev/null 28 | status="$?" 29 | if [ $status -eq 0 ] 30 | then 31 | log_end_msg 1 32 | log_failure_msg \ 33 | "ldapcherryd already started" 34 | return 1 35 | fi 36 | mkdir -p `dirname $PIDFILE` -m 750 37 | chown $USER:$GROUP `dirname $PIDFILE` 38 | if start-stop-daemon -c $USER:$GROUP --start \ 39 | --quiet --pidfile $PIDFILE \ 40 | --oknodo --exec $BIN -- $OPTS 41 | then 42 | log_end_msg 0 || true 43 | return 0 44 | else 45 | log_end_msg 1 || true 46 | return 1 47 | fi 48 | 49 | } 50 | 51 | stop_ldapcherryd(){ 52 | log_daemon_msg "Stopping ldapcherryd" "ldapcherryd" || true 53 | if start-stop-daemon --stop --quiet \ 54 | --pidfile $PIDFILE 55 | then 56 | log_end_msg 0 || true 57 | return 0 58 | else 59 | log_end_msg 1 || true 60 | return 1 61 | fi 62 | } 63 | 64 | case "$1" in 65 | start) 66 | start_ldapcherryd 67 | exit $? 68 | ;; 69 | stop) 70 | stop_ldapcherryd 71 | exit $? 72 | ;; 73 | restart) 74 | stop_ldapcherryd 75 | while pidofproc -p $PIDFILE $BIN >/dev/null 76 | do 77 | sleep 0.5 78 | done 79 | start_ldapcherryd 80 | exit $? 81 | ;; 82 | status) 83 | status_of_proc -p $PIDFILE $BIN "ldapcherryd" \ 84 | && exit 0 || exit $? 85 | ;; 86 | *) 87 | log_action_msg \ 88 | "Usage: /etc/init.d/ldapcherryd {start|stop|restart|status}" \ 89 | || true 90 | exit 1 91 | esac 92 | 93 | exit 0 94 | -------------------------------------------------------------------------------- /goodies/lighttpd.conf: -------------------------------------------------------------------------------- 1 | server.modules += ("mod_proxy") 2 | 3 | $HTTP["host"] == "ldapcherry.kakwa.fr" { 4 | proxy.server = ( "" => 5 | (( "host" => "127.0.0.1", "port" => 8080 )) 6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /goodies/nginx-fastcgi.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default_server; 3 | listen [::]:80 default_server; 4 | 5 | server_name _; 6 | 7 | location / { 8 | fastcgi_param REQUEST_METHOD $request_method; 9 | fastcgi_param QUERY_STRING $query_string; 10 | fastcgi_param CONTENT_TYPE $content_type; 11 | fastcgi_param CONTENT_LENGTH $content_length; 12 | fastcgi_param GATEWAY_INTERFACE CGI/1.1; 13 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; 14 | fastcgi_param REMOTE_ADDR $remote_addr; 15 | fastcgi_param REMOTE_PORT $remote_port; 16 | fastcgi_param SERVER_ADDR $server_addr; 17 | fastcgi_param SERVER_PORT $server_port; 18 | fastcgi_param SERVER_NAME $server_name; 19 | fastcgi_param SERVER_PROTOCOL $server_protocol; 20 | fastcgi_param SCRIPT_FILENAME $fastcgi_script_name; 21 | fastcgi_param PATH_INFO $fastcgi_script_name; 22 | 23 | fastcgi_pass 127.0.0.1:8080; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /goodies/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default_server; 3 | 4 | server_name $hostname; 5 | #access_log /var/log/nginx/dnscherry_access_log; 6 | 7 | location / { 8 | proxy_pass http://127.0.0.1:8080; 9 | proxy_set_header X-Real-IP $remote_addr; 10 | proxy_set_header X-Forwarded-for $proxy_add_x_forwarded_for; 11 | proxy_set_header Host $host:$server_port; 12 | proxy_set_header X-Forwarded-Proto $remote_addr; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /goodies/tag.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd `dirname $0`/../ 4 | 5 | version=`sed -e "s/version\ * = \ *'\(.*\)'.*/\1/;tx;d;:x" ./ldapcherry/version.py` 6 | 7 | git tag "$version" -m "version $version" 8 | git push origin "$version" 9 | -------------------------------------------------------------------------------- /ldapcherry/lclogging.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # vim:set expandtab tabstop=4 shiftwidth=4: 3 | # 4 | # The MIT License (MIT) 5 | # ldapCherry 6 | # Copyright (c) 2014 Carpentier Pierre-Francois 7 | 8 | # Generic imports 9 | import sys 10 | import traceback 11 | import logging 12 | import logging.handlers 13 | import cherrypy 14 | 15 | 16 | # Custom log function to override weird error.log function 17 | # of cherrypy 18 | def syslog_error( 19 | msg='', 20 | context='', 21 | severity=logging.INFO, 22 | traceback=False 23 | ): 24 | 25 | if traceback and msg == '': 26 | msg = 'Python Exception:' 27 | if context == '': 28 | cherrypy.log.error_log.log(severity, msg) 29 | else: 30 | cherrypy.log.error_log.log( 31 | severity, 32 | ' '.join((context, msg)) 33 | ) 34 | if traceback: 35 | import traceback 36 | try: 37 | exc = sys.exc_info() 38 | if exc == (None, None, None): 39 | cherrypy.log.error_log.log(severity, msg) 40 | # log each line of the exception 41 | # in a separate log for lisibility 42 | for l in traceback.format_exception(*exc): 43 | cherrypy.log.error_log.log(severity, l) 44 | finally: 45 | del exc 46 | 47 | 48 | def get_loglevel(level): 49 | """ return logging level object 50 | corresponding to a given level passed as 51 | a string 52 | @str level: name of a syslog log level 53 | @rtype: logging, logging level from logging module 54 | """ 55 | if level == 'debug': 56 | return logging.DEBUG 57 | elif level == 'notice': 58 | return logging.INFO 59 | elif level == 'info': 60 | return logging.INFO 61 | elif level == 'warning' or level == 'warn': 62 | return logging.WARNING 63 | elif level == 'error' or level == 'err': 64 | return logging.ERROR 65 | elif level == 'critical' or level == 'crit': 66 | return logging.CRITICAL 67 | elif level == 'alert': 68 | return logging.CRITICAL 69 | elif level == 'emergency' or level == 'emerg': 70 | return logging.CRITICAL 71 | else: 72 | return logging.INFO 73 | -------------------------------------------------------------------------------- /ldapcherry/ppolicy/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # vim:set expandtab tabstop=4 shiftwidth=4: 3 | # 4 | # The MIT License (MIT) 5 | # LdapCherry 6 | # Copyright (c) 2014 Carpentier Pierre-Francois 7 | 8 | from ldapcherry.exceptions import MissingParameter 9 | 10 | 11 | class PPolicy: 12 | 13 | def __init__(self, config, logger): 14 | """ Password policy constructor 15 | 16 | :param config: the configuration of the ppolicy 17 | :type config: dict {'config key': 'value'} 18 | :param logger: the cherrypy error logger object 19 | :type logger: python logger 20 | """ 21 | pass 22 | 23 | def check(self, password): 24 | """ Check if a password match the ppolicy 25 | 26 | :param password: the password to check 27 | :type password: string 28 | :rtype: dict with keys 'match' a boolean 29 | (True if ppolicy matches, False otherwise) 30 | and 'reason', an explaination string 31 | """ 32 | ret = {'match': True, 'reason': 'no password policy'} 33 | return ret 34 | 35 | def info(self): 36 | """ Give information about the ppolicy 37 | 38 | :rtype: a string describing the ppolicy 39 | """ 40 | ret = "There is no password policy configured" 41 | 42 | def get_param(self, param, default=None): 43 | """ Get a parameter in config (handle default value) 44 | 45 | :param param: name of the parameter to recover 46 | :type param: string 47 | :param default: the default value, raises an exception 48 | if param is not in configuration and default 49 | is None (which is the default value). 50 | :type default: string or None 51 | :rtype: the value of the parameter or the default value if 52 | not set in configuration 53 | """ 54 | if param in self.config: 55 | return self.config[param] 56 | elif default is not None: 57 | return default 58 | else: 59 | raise MissingParameter('ppolicy', param) 60 | -------------------------------------------------------------------------------- /ldapcherry/ppolicy/simple.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # vim:set expandtab tabstop=4 shiftwidth=4: 3 | # 4 | # The MIT License (MIT) 5 | # LdapCherry 6 | # Copyright (c) 2014 Carpentier Pierre-Francois 7 | 8 | import ldapcherry.ppolicy 9 | import re 10 | 11 | 12 | class PPolicy(ldapcherry.ppolicy.PPolicy): 13 | 14 | def __init__(self, config, logger): 15 | self.config = config 16 | self.min_length = self.get_param('min_length') 17 | self.min_upper = self.get_param('min_upper') 18 | self.min_digit = self.get_param('min_digit') 19 | 20 | def check(self, password): 21 | if len(password) < self.min_length: 22 | return {'match': False, 'reason': 'Password too short'} 23 | if len(re.findall(r'[A-Z]', password)) < self.min_upper: 24 | return { 25 | 'match': False, 26 | 'reason': 'Not enough upper case characters' 27 | } 28 | if len(re.findall(r'[0-9]', password)) < self.min_digit: 29 | return {'match': False, 'reason': 'Not enough digits'} 30 | return {'match': True, 'reason': 'password ok'} 31 | 32 | def info(self): 33 | return \ 34 | "* Minimum length: %(len)d\n" \ 35 | "* Minimum number of uppercase characters: %(upper)d\n" \ 36 | "* Minimum number of digits: %(digit)d" % { 37 | 'upper': self.min_upper, 38 | 'len': self.min_length, 39 | 'digit': self.min_digit 40 | } 41 | -------------------------------------------------------------------------------- /ldapcherry/pyyamlwrapper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | import sys 5 | import yaml 6 | from yaml.error import * 7 | from yaml.nodes import * 8 | from yaml.reader import * 9 | from yaml.scanner import * 10 | from yaml.parser import * 11 | from yaml.composer import * 12 | from yaml.constructor import * 13 | from yaml.resolver import * 14 | 15 | 16 | class RelationError(Exception): 17 | def __init__(self, key, value): 18 | self.key = key 19 | self.value = value 20 | 21 | 22 | class DumplicatedKey(Exception): 23 | def __init__(self, host, key): 24 | self.host = host 25 | self.key = key 26 | 27 | 28 | try: 29 | from yaml import CLoader as Loader, CDumper as Dumper 30 | except ImportError: 31 | from yaml import Loader, Dumper 32 | 33 | 34 | # PyYaml wrapper that loads yaml files throwing an exception 35 | # if a key is dumplicated 36 | class MyLoader(Reader, Scanner, Parser, Composer, Constructor, Resolver): 37 | 38 | def __init__(self, stream): 39 | Reader.__init__(self, stream) 40 | Scanner.__init__(self) 41 | Parser.__init__(self) 42 | Composer.__init__(self) 43 | Constructor.__init__(self) 44 | Resolver.__init__(self) 45 | 46 | def construct_mapping(self, node, deep=False): 47 | exc = sys.exc_info()[1] 48 | if not isinstance(node, MappingNode): 49 | raise ConstructorError( 50 | None, 51 | None, 52 | "expected a mapping node, but found %s" % node.id, 53 | node.start_mark 54 | ) 55 | mapping = {} 56 | for key_node, value_node in node.value: 57 | key = self.construct_object(key_node, deep=deep) 58 | try: 59 | hash(key) 60 | except TypeError: 61 | raise ConstructorError( 62 | "while constructing a mapping", 63 | node.start_mark, 64 | "found unacceptable key (%s)" % exc, key_node.start_mark 65 | ) 66 | value = self.construct_object(value_node, deep=deep) 67 | if key in mapping: 68 | raise DumplicatedKey(key, '') 69 | mapping[key] = value 70 | return mapping 71 | 72 | 73 | def loadNoDump(stream): 74 | loader = MyLoader(stream) 75 | try: 76 | return loader.get_single_data() 77 | finally: 78 | loader.dispose() 79 | 80 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 81 | -------------------------------------------------------------------------------- /ldapcherry/version.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # vim:set expandtab tabstop=4 shiftwidth=4: 3 | # 4 | # The MIT License (MIT) 5 | # ldapCherry 6 | # Copyright (c) 2014 Carpentier Pierre-Francois 7 | 8 | version = '1.1.1' 9 | -------------------------------------------------------------------------------- /misc/debug_attributes.py: -------------------------------------------------------------------------------- 1 | from ldapcherry.attributes import Attributes 2 | from ldapcherry.exceptions import DumplicateRoleKey, MissingKey, DumplicateRoleContent, MissingRolesFile 3 | from ldapcherry.pyyamlwrapper import DumplicatedKey, RelationError 4 | from yaml import load, dump 5 | import yaml 6 | 7 | try: 8 | from yaml import CLoader as Loader, CDumper as Dumper 9 | except ImportError: 10 | from yaml import Loader, Dumper 11 | 12 | class CustomDumper(yaml.SafeDumper): 13 | "A custom YAML dumper that never emits aliases" 14 | 15 | def ignore_aliases(self, _data): 16 | return True 17 | 18 | try: 19 | inv = Attributes('./tests/cfg/attributes.yml') 20 | #inv = Attributes('./tests/cfg/attributes_wrong_type.yml') 21 | except Exception as e: 22 | print e.log 23 | 24 | print inv.backend_attributes 25 | -------------------------------------------------------------------------------- /misc/debug_lc.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import sys 3 | from sets import Set 4 | from ldapcherry import LdapCherry 5 | from ldapcherry.exceptions import DumplicateRoleKey, MissingKey, DumplicateRoleContent, MissingRolesFile, MissingRole 6 | from ldapcherry.pyyamlwrapper import DumplicatedKey, RelationError 7 | import cherrypy 8 | from cherrypy.process import plugins, servers 9 | from cherrypy import Application 10 | import logging 11 | 12 | # monkey patching cherrypy to disable config interpolation 13 | def new_as_dict(self, raw=True, vars=None): 14 | """Convert an INI file to a dictionary""" 15 | # Load INI file into a dict 16 | result = {} 17 | for section in self.sections(): 18 | if section not in result: 19 | result[section] = {} 20 | for option in self.options(section): 21 | value = self.get(section, option, raw=raw, vars=vars) 22 | try: 23 | value = cherrypy.lib.reprconf.unrepr(value) 24 | except Exception: 25 | x = sys.exc_info()[1] 26 | msg = ("Config error in section: %r, option: %r, " 27 | "value: %r. Config values must be valid Python." % 28 | (section, option, value)) 29 | raise ValueError(msg, x.__class__.__name__, x.args) 30 | result[section][option] = value 31 | return result 32 | cherrypy.lib.reprconf.Parser.as_dict = new_as_dict 33 | 34 | 35 | def loadconf(configfile, instance): 36 | app = cherrypy.tree.mount(instance, '/', configfile) 37 | cherrypy.config.update(configfile) 38 | instance.reload(app.config) 39 | 40 | app = LdapCherry() 41 | loadconf('./tests/cfg/ldapcherry.ini', app) 42 | ret = app._get_user('ssmith') 43 | print ret 44 | 45 | } 46 | 47 | form = {'groups': {}, 'attrs': {'password1': u'password☭', 'password2': u'password☭', 'shell': u'/bin/zsh', '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'test'}, 'roles': {'admin-lv3': u'on', 'admin-lv2': u'on', 'users': u'on'}} 48 | -------------------------------------------------------------------------------- /misc/debug_ldapbackend.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from __future__ import with_statement 5 | from __future__ import unicode_literals 6 | 7 | import pytest 8 | import sys 9 | from sets import Set 10 | from ldapcherry.backend.backendLdap import Backend 11 | from ldapcherry.exceptions import * 12 | import cherrypy 13 | import logging 14 | from ldap import SERVER_DOWN 15 | 16 | cfg = { 17 | 'module' : 'ldapcherry.backend.ldap', 18 | 'groupdn' : 'ou=Groups,dc=example,dc=org', 19 | 'userdn' : 'ou=People,dc=example,dc=org', 20 | 'binddn' : 'cn=dnscherry,dc=example,dc=org', 21 | 'password' : 'password', 22 | 'uri' : 'ldap://ldap.ldapcherry.org:390', 23 | 'ca' : './tests/test_env/etc/ldapcherry/TEST-cacert.pem', 24 | 'starttls' : 'off', 25 | 'checkcert' : 'off', 26 | 'user_filter_tmpl' : '(uid=%(username)s)', 27 | 'group_filter_tmpl' : '(member=%(userdn)s)', 28 | 'search_filter_tmpl' : '(|(uid=%(searchstring)s*)(sn=%(searchstring)s*))', 29 | 'objectclasses' : 'top, person, organizationalPerson, simpleSecurityObject, posixAccount', 30 | 'dn_user_attr' : 'uid', 31 | 'group_attr.uniqMember' : "%(dn)s", 32 | 'group_attr.memberUid' : "%(uid)s", 33 | 34 | } 35 | 36 | def syslog_error(msg='', context='', 37 | severity=logging.INFO, traceback=False): 38 | pass 39 | 40 | cherrypy.log.error = syslog_error 41 | attr = ['shéll', 'cn', 'uid', 'uidNumber', 'gidNumber', 'home', 'userPassword', 'givenName', 'email', 'sn'] 42 | 43 | cherrypy.log.error = syslog_error 44 | 45 | inv = Backend(cfg, cherrypy.log, 'ldap', attr, 'uid') 46 | print inv.get_user('jwatson') 47 | print inv.get_groups('jwatson') 48 | print inv.search('smit') 49 | user = { 50 | 'uid': 'test', 51 | 'sn': 'test', 52 | 'cn': 'test', 53 | 'userPassword': 'test', 54 | 'uidNumber': '42', 55 | 'gidNumber': '42', 56 | 'homeDirectory': '/home/test/' 57 | } 58 | inv.add_user(user) 59 | print inv.get_user('test') 60 | print inv.get_groups('test') 61 | inv.del_user('test') 62 | 63 | groups = [ 64 | 'cn=hrpeople,ou=Groups,dc=example,dc=org', 65 | 'cn=itpeople,ou=Groups,dc=example,dc=org', 66 | ] 67 | inv.add_to_groups('jwatson', groups) 68 | ret = inv.get_groups('jwatson') 69 | print ret 70 | inv.del_from_groups('jwatson', ['cn=hrpeople,ou=Groups,dc=example,dc=org']) 71 | inv.del_from_groups('jwatson', ['cn=hrpeople,ou=Groups,dc=example,dc=org']) 72 | 73 | 74 | print inv.group_attrs 75 | -------------------------------------------------------------------------------- /misc/debug_roles.py: -------------------------------------------------------------------------------- 1 | from ldapcherry.roles import Roles 2 | from ldapcherry.exceptions import DumplicateRoleKey, MissingKey, DumplicateRoleContent, MissingRolesFile 3 | from ldapcherry.pyyamlwrapper import DumplicatedKey, RelationError 4 | from yaml import load, dump 5 | import yaml 6 | 7 | try: 8 | from yaml import CLoader as Loader, CDumper as Dumper 9 | except ImportError: 10 | from yaml import Loader, Dumper 11 | 12 | class CustomDumper(yaml.SafeDumper): 13 | "A custom YAML dumper that never emits aliases" 14 | 15 | def ignore_aliases(self, _data): 16 | return True 17 | 18 | inv = Roles('./conf/roles.yml') 19 | print 20 | print inv.dump_nest() 21 | 22 | groups = { 23 | 'ad' : ['Domain Users', 'Domain Users 2'], 24 | 'ldap': ['cn=users,ou=group,dc=example,dc=com'] 25 | } 26 | 27 | print inv.get_roles(groups) 28 | 29 | groups = { 30 | 'ad' : ['Domain Users', 'Domain Users 2'], 31 | 'ldap': ['cn=users,ou=group,dc=example,dc=com', 32 | 'cn=nagios admins,ou=group,dc=example,dc=com', 33 | 'cn=developers,ou=group,dc=example,dc=com', 34 | ], 35 | 'toto': ['not a group'], 36 | } 37 | 38 | 39 | print inv.get_roles(groups) 40 | 41 | print inv.get_allroles() 42 | 43 | print inv.get_backends() 44 | -------------------------------------------------------------------------------- /requirements-el7.txt: -------------------------------------------------------------------------------- 1 | CherryPy>=3.0.0 2 | PyYAML 3 | Mako 4 | python-ldap==3.4.0 5 | -------------------------------------------------------------------------------- /requirements-stretch.txt: -------------------------------------------------------------------------------- 1 | CherryPy>=3.0.0 2 | PyYAML 3 | Mako 4 | python-ldap==3.4.0 5 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | CherryPy>=3.0.0 2 | PyYAML 3 | Mako 4 | python-ldap 5 | -------------------------------------------------------------------------------- /resources/static/css/custom.css: -------------------------------------------------------------------------------- 1 | .btn.red { 2 | background-color: hsl(8, 76%, 30%) !important; 3 | background-repeat: repeat-x; 4 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#e0381e", endColorstr="#862112"); 5 | background-image: -khtml-gradient(linear, left top, left bottom, from(#e0381e), to(#862112)); 6 | background-image: -moz-linear-gradient(top, #e0381e, #862112); 7 | background-image: -ms-linear-gradient(top, #e0381e, #862112); 8 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #e0381e), color-stop(100%, #862112)); 9 | background-image: -webkit-linear-gradient(top, #e0381e, #862112); 10 | background-image: -o-linear-gradient(top, #e0381e, #862112); 11 | background-image: linear-gradient(#e0381e, #862112); 12 | border-color: #862112 #862112 hsl(8, 76%, 25%); 13 | color: #fff !important; 14 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.33); 15 | -webkit-font-smoothing: antialiased; 16 | height: 34px; 17 | } 18 | 19 | .btn.pad { 20 | padding: inherit; 21 | } 22 | 23 | .btn.blue { 24 | background-color: hsl(201, 100%, 30%) !important; 25 | background-repeat: repeat-x; 26 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#00a5ff", endColorstr="#006399"); 27 | background-image: -khtml-gradient(linear, left top, left bottom, from(#00a5ff), to(#006399)); 28 | background-image: -moz-linear-gradient(top, #00a5ff, #006399); 29 | background-image: -ms-linear-gradient(top, #00a5ff, #006399); 30 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #00a5ff), color-stop(100%, #006399)); 31 | background-image: -webkit-linear-gradient(top, #00a5ff, #006399); 32 | background-image: -o-linear-gradient(top, #00a5ff, #006399); 33 | background-image: linear-gradient(#00a5ff, #006399); 34 | border-color: #006399 #006399 hsl(201, 100%, 25%); 35 | color: #fff !important; 36 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.33); 37 | -webkit-font-smoothing: antialiased; 38 | height: 34px; 39 | } 40 | 41 | .btn.green { 42 | background-color: hsl(97, 68%, 30%) !important; 43 | background-repeat: repeat-x; 44 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#6bd628", endColorstr="#408018"); 45 | background-image: -khtml-gradient(linear, left top, left bottom, from(#6bd628), to(#408018)); 46 | background-image: -moz-linear-gradient(top, #6bd628, #408018); 47 | background-image: -ms-linear-gradient(top, #6bd628, #408018); 48 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #6bd628), color-stop(100%, #408018)); 49 | background-image: -webkit-linear-gradient(top, #6bd628, #408018); 50 | background-image: -o-linear-gradient(top, #6bd628, #408018); 51 | background-image: linear-gradient(#6bd628, #408018); 52 | border-color: #408018 #408018 hsl(97, 68%, 25%); 53 | color: #fff !important; 54 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.33); 55 | -webkit-font-smoothing: antialiased; 56 | height: 34px; 57 | } 58 | 59 | .top-buffer { margin-top:30px; } 60 | .bottom-buffer { margin-bottom:30px; } 61 | 62 | div#footer { 63 | position:fixed; 64 | bottom: 0px; 65 | padding-top: 10px; 66 | border-top: 1px solid gray; 67 | width: 100%; 68 | z-index: 1; 69 | background-color: #f5f5f5; 70 | font-size: 12px; 71 | text-align: center; 72 | } 73 | 74 | .form-control:-moz-read-only { /* For Firefox */ 75 | background-color: white; 76 | cursor: default; 77 | } 78 | 79 | .form-control:read-only { 80 | background-color: white; 81 | cursor: default; 82 | } 83 | -------------------------------------------------------------------------------- /resources/static/css/style.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/resources/static/css/style.css -------------------------------------------------------------------------------- /resources/static/css/tablesorter-bootstrap.css: -------------------------------------------------------------------------------- 1 | /************* 2 | Default Theme 3 | *************/ 4 | /* header */ 5 | .tablesorter-default .tablesorter-header { 6 | background-image: url(data:image/gif;base64,R0lGODlhFQAJAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAkAAAIXjI+AywnaYnhUMoqt3gZXPmVg94yJVQAAOw==); 7 | background-position: center right; 8 | background-repeat: no-repeat; 9 | cursor: pointer; 10 | white-space: normal; 11 | padding: 4px 20px 4px 4px; 12 | } 13 | .tablesorter-default thead .headerSortUp, 14 | .tablesorter-default thead .tablesorter-headerSortUp, 15 | .tablesorter-default thead .tablesorter-headerAsc { 16 | background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7); 17 | border-bottom: #000 2px solid; 18 | } 19 | .tablesorter-default thead .headerSortDown, 20 | .tablesorter-default thead .tablesorter-headerSortDown, 21 | .tablesorter-default thead .tablesorter-headerDesc { 22 | background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7); 23 | border-bottom: #000 2px solid; 24 | } 25 | .tablesorter-default thead .sorter-false { 26 | background-image: none; 27 | cursor: default; 28 | padding: 4px; 29 | } 30 | 31 | /* table processing indicator */ 32 | .tablesorter-default .tablesorter-processing { 33 | background-position: center center !important; 34 | background-repeat: no-repeat !important; 35 | /* background-image: url(../addons/pager/icons/loading.gif) !important; */ 36 | background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; 37 | } 38 | 39 | 40 | -------------------------------------------------------------------------------- /resources/static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/resources/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /resources/static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/resources/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /resources/static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/resources/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /resources/static/img/apple-touch-icon-114-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/resources/static/img/apple-touch-icon-114-precomposed.png -------------------------------------------------------------------------------- /resources/static/img/apple-touch-icon-144-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/resources/static/img/apple-touch-icon-144-precomposed.png -------------------------------------------------------------------------------- /resources/static/img/apple-touch-icon-57-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/resources/static/img/apple-touch-icon-57-precomposed.png -------------------------------------------------------------------------------- /resources/static/img/apple-touch-icon-72-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/resources/static/img/apple-touch-icon-72-precomposed.png -------------------------------------------------------------------------------- /resources/static/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/resources/static/img/favicon.png -------------------------------------------------------------------------------- /resources/static/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/resources/static/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /resources/static/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/resources/static/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /resources/static/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/resources/static/img/icon.png -------------------------------------------------------------------------------- /resources/static/img/icon.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/resources/static/img/icon.xcf -------------------------------------------------------------------------------- /resources/static/js/alignforms.js: -------------------------------------------------------------------------------- 1 | var classes = ["lcform-col-1", "lcform-col-2"]; 2 | var j, lenj = classes.length; 3 | for(j=0; j < lenj; j++){ 4 | var formSelector = classes[j]; 5 | var forms = document.getElementsByClassName(formSelector); 6 | //console.log(formSelector); 7 | //console.log(forms); 8 | if (forms.length > 0){ 9 | forms[0].style.removeProperty("display"); 10 | var InputGroups = forms[0].getElementsByClassName("input-group-addon"); 11 | //console.log(InputGroups); 12 | var i, len = InputGroups.length; 13 | var longest = 0; 14 | for(i=0; i < len; i++){ 15 | if (InputGroups[i].id !== "basic-addon-password2"){ 16 | longest = longest < InputGroups[i].clientWidth ? InputGroups[i].clientWidth : longest; 17 | } 18 | } 19 | for(i=0; i < len; i++){ 20 | InputGroups[i].style.minWidth = (longest + 0) + "px"; 21 | } 22 | //console.log(longest); 23 | } 24 | } 25 | //console.log("end_re"); 26 | -------------------------------------------------------------------------------- /resources/static/js/html5shiv.js: -------------------------------------------------------------------------------- 1 | /* 2 | HTML5 Shiv v3.6.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | (function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag(); 5 | a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/\w+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x"; 6 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| 7 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup main mark meter nav output progress section summary time video",version:"3.6.2",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);if(g)return a.createDocumentFragment(); 8 | for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d>16)+(b>>16)+(c>>16);return d<<16|65535&c}function c(a,b){return a<>>32-b}function d(a,d,e,f,g,h){return b(c(b(b(d,a),b(f,h)),g),e)}function e(a,b,c,e,f,g,h){return d(b&c|~b&e,a,b,f,g,h)}function f(a,b,c,e,f,g,h){return d(b&e|c&~e,a,b,f,g,h)}function g(a,b,c,e,f,g,h){return d(b^c^e,a,b,f,g,h)}function h(a,b,c,e,f,g,h){return d(c^(b|~e),a,b,f,g,h)}function i(a,c){a[c>>5]|=128<>>9<<4)+14]=c;var d,i,j,k,l,m=1732584193,n=-271733879,o=-1732584194,p=271733878;for(d=0;d>5]>>>b%32&255);return c}function k(a){var b,c=[];for(c[(a.length>>2)-1]=void 0,b=0;b>5]|=(255&a.charCodeAt(b/8))<16&&(e=i(e,8*a.length)),c=0;16>c;c+=1)f[c]=909522486^e[c],g[c]=1549556828^e[c];return d=i(f.concat(k(b)),512+8*b.length),j(i(g.concat(d),640))}function n(a){var b,c,d="0123456789abcdef",e="";for(c=0;c>>4&15)+d.charAt(15&b);return e}function o(a){return unescape(encodeURIComponent(a))}function p(a){return l(o(a))}function q(a){return n(p(a))}function r(a,b){return m(o(a),o(b))}function s(a,b){return n(r(a,b))}function t(a,b,c){return b?c?r(b,a):s(b,a):c?p(a):q(a)}"function"==typeof define&&define.amd?define(function(){return t}):a.md5=t}(this); -------------------------------------------------------------------------------- /resources/static/js/ppolicy.js: -------------------------------------------------------------------------------- 1 | $('#form').validator({ 2 | custom: { 3 | 'ppolicy': function($el) { 4 | if(! $el.prop('required') && $el.val() == 0){ 5 | return true; 6 | }; 7 | var $ret = 'PPolicy error'; 8 | $.ajax({ 9 | url: '/checkppolicy', 10 | type: 'POST', 11 | dataType: 'json', 12 | async: false, 13 | data: 'pwd=' + encodeURIComponent($el.val()), 14 | success: function(data) { 15 | $ret = data; 16 | }, 17 | error: function(jqXHR, exception) { 18 | switch (jqXHR.status) { 19 | case 400: 20 | $ret = {"reason":"Javascript ppolicy.js error","match":false}; 21 | break; 22 | case 403: 23 | $ret = {"reason":"Session expired, you must reconnect","match":false}; 24 | break; 25 | case 500: 26 | $ret = {"reason":"Server error","match":false}; 27 | break; 28 | default: 29 | $ret = {"reason":"Unknown error [" + jqXHR.status + "], check logs","match":false}; 30 | } 31 | } 32 | }); 33 | this.options.errors['ppolicy'] = $ret['reason']; 34 | return $ret['match']; 35 | } 36 | }, 37 | errors: { 38 | 'ppolicy': 'PPolicy error', 39 | } 40 | }) 41 | 42 | // vim:set expandtab tabstop=4 shiftwidth=4: 43 | -------------------------------------------------------------------------------- /resources/static/js/scripts.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/resources/static/js/scripts.js -------------------------------------------------------------------------------- /resources/static/less/alerts.less: -------------------------------------------------------------------------------- 1 | // 2 | // Alerts 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base styles 7 | // ------------------------- 8 | 9 | .alert { 10 | padding: @alert-padding; 11 | margin-bottom: @line-height-computed; 12 | border: 1px solid transparent; 13 | border-radius: @alert-border-radius; 14 | 15 | // Headings for larger alerts 16 | h4 { 17 | margin-top: 0; 18 | // Specified for the h4 to prevent conflicts of changing @headings-color 19 | color: inherit; 20 | } 21 | // Provide class for links that match alerts 22 | .alert-link { 23 | font-weight: @alert-link-font-weight; 24 | } 25 | 26 | // Improve alignment and spacing of inner content 27 | > p, 28 | > ul { 29 | margin-bottom: 0; 30 | } 31 | > p + p { 32 | margin-top: 5px; 33 | } 34 | } 35 | 36 | // Dismissable alerts 37 | // 38 | // Expand the right padding and account for the close button's positioning. 39 | 40 | .alert-dismissable { 41 | padding-right: (@alert-padding + 20); 42 | 43 | // Adjust close link position 44 | .close { 45 | position: relative; 46 | top: -2px; 47 | right: -21px; 48 | color: inherit; 49 | } 50 | } 51 | 52 | // Alternate styles 53 | // 54 | // Generate contextual modifier classes for colorizing the alert. 55 | 56 | .alert-success { 57 | .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text); 58 | } 59 | .alert-info { 60 | .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text); 61 | } 62 | .alert-warning { 63 | .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text); 64 | } 65 | .alert-danger { 66 | .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text); 67 | } 68 | -------------------------------------------------------------------------------- /resources/static/less/badges.less: -------------------------------------------------------------------------------- 1 | // 2 | // Badges 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base classes 7 | .badge { 8 | display: inline-block; 9 | min-width: 10px; 10 | padding: 3px 7px; 11 | font-size: @font-size-small; 12 | font-weight: @badge-font-weight; 13 | color: @badge-color; 14 | line-height: @badge-line-height; 15 | vertical-align: baseline; 16 | white-space: nowrap; 17 | text-align: center; 18 | background-color: @badge-bg; 19 | border-radius: @badge-border-radius; 20 | 21 | // Empty badges collapse automatically (not available in IE8) 22 | &:empty { 23 | display: none; 24 | } 25 | } 26 | 27 | // Hover state, but only for links 28 | a.badge { 29 | &:hover, 30 | &:focus { 31 | color: @badge-link-hover-color; 32 | text-decoration: none; 33 | cursor: pointer; 34 | } 35 | } 36 | 37 | // Quick fix for labels/badges in buttons 38 | .btn .badge { 39 | position: relative; 40 | top: -1px; 41 | } 42 | 43 | // Account for counters in navs 44 | a.list-group-item.active > .badge, 45 | .nav-pills > .active > a > .badge { 46 | color: @badge-active-color; 47 | background-color: @badge-active-bg; 48 | } 49 | .nav-pills > li > a > .badge { 50 | margin-left: 3px; 51 | } 52 | -------------------------------------------------------------------------------- /resources/static/less/bootstrap.less: -------------------------------------------------------------------------------- 1 | // Core variables and mixins 2 | @import "variables.less"; 3 | @import "mixins.less"; 4 | 5 | // Reset 6 | @import "normalize.less"; 7 | @import "print.less"; 8 | 9 | // Core CSS 10 | @import "scaffolding.less"; 11 | @import "type.less"; 12 | @import "code.less"; 13 | @import "grid.less"; 14 | @import "tables.less"; 15 | @import "forms.less"; 16 | @import "buttons.less"; 17 | 18 | // Components 19 | @import "component-animations.less"; 20 | @import "glyphicons.less"; 21 | @import "dropdowns.less"; 22 | @import "button-groups.less"; 23 | @import "input-groups.less"; 24 | @import "navs.less"; 25 | @import "navbar.less"; 26 | @import "breadcrumbs.less"; 27 | @import "pagination.less"; 28 | @import "pager.less"; 29 | @import "labels.less"; 30 | @import "badges.less"; 31 | @import "jumbotron.less"; 32 | @import "thumbnails.less"; 33 | @import "alerts.less"; 34 | @import "progress-bars.less"; 35 | @import "media.less"; 36 | @import "list-group.less"; 37 | @import "panels.less"; 38 | @import "wells.less"; 39 | @import "close.less"; 40 | 41 | // Components w/ JavaScript 42 | @import "modals.less"; 43 | @import "tooltip.less"; 44 | @import "popovers.less"; 45 | @import "carousel.less"; 46 | 47 | // Utility classes 48 | @import "utilities.less"; 49 | @import "responsive-utilities.less"; 50 | -------------------------------------------------------------------------------- /resources/static/less/breadcrumbs.less: -------------------------------------------------------------------------------- 1 | // 2 | // Breadcrumbs 3 | // -------------------------------------------------- 4 | 5 | 6 | .breadcrumb { 7 | padding: 8px 15px; 8 | margin-bottom: @line-height-computed; 9 | list-style: none; 10 | background-color: @breadcrumb-bg; 11 | border-radius: @border-radius-base; 12 | > li { 13 | display: inline-block; 14 | + li:before { 15 | content: "@{breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space 16 | padding: 0 5px; 17 | color: @breadcrumb-color; 18 | } 19 | } 20 | > .active { 21 | color: @breadcrumb-active-color; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /resources/static/less/close.less: -------------------------------------------------------------------------------- 1 | // 2 | // Close icons 3 | // -------------------------------------------------- 4 | 5 | 6 | .close { 7 | float: right; 8 | font-size: (@font-size-base * 1.5); 9 | font-weight: @close-font-weight; 10 | line-height: 1; 11 | color: @close-color; 12 | text-shadow: @close-text-shadow; 13 | .opacity(.2); 14 | 15 | &:hover, 16 | &:focus { 17 | color: @close-color; 18 | text-decoration: none; 19 | cursor: pointer; 20 | .opacity(.5); 21 | } 22 | 23 | // Additional properties for button version 24 | // iOS requires the button element instead of an anchor tag. 25 | // If you want the anchor version, it requires `href="#"`. 26 | button& { 27 | padding: 0; 28 | cursor: pointer; 29 | background: transparent; 30 | border: 0; 31 | -webkit-appearance: none; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /resources/static/less/code.less: -------------------------------------------------------------------------------- 1 | // 2 | // Code (inline and block) 3 | // -------------------------------------------------- 4 | 5 | 6 | // Inline and block code styles 7 | code, 8 | kbd, 9 | pre, 10 | samp { 11 | font-family: @font-family-monospace; 12 | } 13 | 14 | // Inline code 15 | code { 16 | padding: 2px 4px; 17 | font-size: 90%; 18 | color: @code-color; 19 | background-color: @code-bg; 20 | white-space: nowrap; 21 | border-radius: @border-radius-base; 22 | } 23 | 24 | // Blocks of code 25 | pre { 26 | display: block; 27 | padding: ((@line-height-computed - 1) / 2); 28 | margin: 0 0 (@line-height-computed / 2); 29 | font-size: (@font-size-base - 1); // 14px to 13px 30 | line-height: @line-height-base; 31 | word-break: break-all; 32 | word-wrap: break-word; 33 | color: @pre-color; 34 | background-color: @pre-bg; 35 | border: 1px solid @pre-border-color; 36 | border-radius: @border-radius-base; 37 | 38 | // Account for some code outputs that place code tags in pre tags 39 | code { 40 | padding: 0; 41 | font-size: inherit; 42 | color: inherit; 43 | white-space: pre-wrap; 44 | background-color: transparent; 45 | border-radius: 0; 46 | } 47 | } 48 | 49 | // Enable scrollable blocks of code 50 | .pre-scrollable { 51 | max-height: @pre-scrollable-max-height; 52 | overflow-y: scroll; 53 | } 54 | -------------------------------------------------------------------------------- /resources/static/less/component-animations.less: -------------------------------------------------------------------------------- 1 | // 2 | // Component animations 3 | // -------------------------------------------------- 4 | 5 | // Heads up! 6 | // 7 | // We don't use the `.opacity()` mixin here since it causes a bug with text 8 | // fields in IE7-8. Source: https://github.com/twitter/bootstrap/pull/3552. 9 | 10 | .fade { 11 | opacity: 0; 12 | .transition(opacity .15s linear); 13 | &.in { 14 | opacity: 1; 15 | } 16 | } 17 | 18 | .collapse { 19 | display: none; 20 | &.in { 21 | display: block; 22 | } 23 | } 24 | .collapsing { 25 | position: relative; 26 | height: 0; 27 | overflow: hidden; 28 | .transition(height .35s ease); 29 | } 30 | -------------------------------------------------------------------------------- /resources/static/less/grid.less: -------------------------------------------------------------------------------- 1 | // 2 | // Grid system 3 | // -------------------------------------------------- 4 | 5 | // Set the container width, and override it for fixed navbars in media queries 6 | .container { 7 | .container-fixed(); 8 | } 9 | 10 | // mobile first defaults 11 | .row { 12 | .make-row(); 13 | } 14 | 15 | // Common styles for small and large grid columns 16 | .make-grid-columns(); 17 | 18 | 19 | // Extra small grid 20 | // 21 | // Grid classes for extra small devices like smartphones. No offset, push, or 22 | // pull classes are present here due to the size of the target. 23 | // 24 | // Note that `.col-xs-12` doesn't get floated on purpose--there's no need since 25 | // it's full-width. 26 | 27 | .make-grid-columns-float(xs); 28 | .make-grid(@grid-columns, xs, width); 29 | .make-grid(@grid-columns, xs, pull); 30 | .make-grid(@grid-columns, xs, push); 31 | .make-grid(@grid-columns, xs, offset); 32 | 33 | 34 | // Small grid 35 | // 36 | // Columns, offsets, pushes, and pulls for the small device range, from phones 37 | // to tablets. 38 | // 39 | // Note that `.col-sm-12` doesn't get floated on purpose--there's no need since 40 | // it's full-width. 41 | 42 | @media (min-width: @screen-sm-min) { 43 | .container { 44 | width: @container-sm; 45 | } 46 | 47 | .make-grid-columns-float(sm); 48 | .make-grid(@grid-columns, sm, width); 49 | .make-grid(@grid-columns, sm, pull); 50 | .make-grid(@grid-columns, sm, push); 51 | .make-grid(@grid-columns, sm, offset); 52 | } 53 | 54 | 55 | // Medium grid 56 | // 57 | // Columns, offsets, pushes, and pulls for the desktop device range. 58 | // 59 | // Note that `.col-md-12` doesn't get floated on purpose--there's no need since 60 | // it's full-width. 61 | 62 | @media (min-width: @screen-md-min) { 63 | .container { 64 | width: @container-md; 65 | } 66 | 67 | .make-grid-columns-float(md); 68 | .make-grid(@grid-columns, md, width); 69 | .make-grid(@grid-columns, md, pull); 70 | .make-grid(@grid-columns, md, push); 71 | .make-grid(@grid-columns, md, offset); 72 | } 73 | 74 | 75 | // Large grid 76 | // 77 | // Columns, offsets, pushes, and pulls for the large desktop device range. 78 | // 79 | // Note that `.col-lg-12` doesn't get floated on purpose--there's no need since 80 | // it's full-width. 81 | 82 | @media (min-width: @screen-lg-min) { 83 | .container { 84 | width: @container-lg; 85 | } 86 | 87 | .make-grid-columns-float(lg); 88 | .make-grid(@grid-columns, lg, width); 89 | .make-grid(@grid-columns, lg, pull); 90 | .make-grid(@grid-columns, lg, push); 91 | .make-grid(@grid-columns, lg, offset); 92 | } 93 | 94 | -------------------------------------------------------------------------------- /resources/static/less/input-groups.less: -------------------------------------------------------------------------------- 1 | // 2 | // Input groups 3 | // -------------------------------------------------- 4 | 5 | // Base styles 6 | // ------------------------- 7 | .input-group { 8 | position: relative; // For dropdowns 9 | display: table; 10 | border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table 11 | 12 | // Undo padding and float of grid classes 13 | &.col { 14 | float: none; 15 | padding-left: 0; 16 | padding-right: 0; 17 | } 18 | 19 | .form-control { 20 | width: 100%; 21 | margin-bottom: 0; 22 | } 23 | } 24 | 25 | // Sizing options 26 | // 27 | // Remix the default form control sizing classes into new ones for easier 28 | // manipulation. 29 | 30 | .input-group-lg > .form-control, 31 | .input-group-lg > .input-group-addon, 32 | .input-group-lg > .input-group-btn > .btn { .input-lg(); } 33 | .input-group-sm > .form-control, 34 | .input-group-sm > .input-group-addon, 35 | .input-group-sm > .input-group-btn > .btn { .input-sm(); } 36 | 37 | 38 | // Display as table-cell 39 | // ------------------------- 40 | .input-group-addon, 41 | .input-group-btn, 42 | .input-group .form-control { 43 | display: table-cell; 44 | 45 | &:not(:first-child):not(:last-child) { 46 | border-radius: 0; 47 | } 48 | } 49 | // Addon and addon wrapper for buttons 50 | .input-group-addon, 51 | .input-group-btn { 52 | width: 1%; 53 | white-space: nowrap; 54 | vertical-align: middle; // Match the inputs 55 | } 56 | 57 | // Text input groups 58 | // ------------------------- 59 | .input-group-addon { 60 | padding: @padding-base-vertical @padding-base-horizontal; 61 | font-size: @font-size-base; 62 | font-weight: normal; 63 | line-height: 1; 64 | color: @input-color; 65 | text-align: center; 66 | background-color: @input-group-addon-bg; 67 | border: 1px solid @input-group-addon-border-color; 68 | border-radius: @border-radius-base; 69 | 70 | // Sizing 71 | &.input-sm { 72 | padding: @padding-small-vertical @padding-small-horizontal; 73 | font-size: @font-size-small; 74 | border-radius: @border-radius-small; 75 | } 76 | &.input-lg { 77 | padding: @padding-large-vertical @padding-large-horizontal; 78 | font-size: @font-size-large; 79 | border-radius: @border-radius-large; 80 | } 81 | 82 | // Nuke default margins from checkboxes and radios to vertically center within. 83 | input[type="radio"], 84 | input[type="checkbox"] { 85 | margin-top: 0; 86 | } 87 | } 88 | 89 | // Reset rounded corners 90 | .input-group .form-control:first-child, 91 | .input-group-addon:first-child, 92 | .input-group-btn:first-child > .btn, 93 | .input-group-btn:first-child > .dropdown-toggle, 94 | .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle) { 95 | .border-right-radius(0); 96 | } 97 | .input-group-addon:first-child { 98 | border-right: 0; 99 | } 100 | .input-group .form-control:last-child, 101 | .input-group-addon:last-child, 102 | .input-group-btn:last-child > .btn, 103 | .input-group-btn:last-child > .dropdown-toggle, 104 | .input-group-btn:first-child > .btn:not(:first-child) { 105 | .border-left-radius(0); 106 | } 107 | .input-group-addon:last-child { 108 | border-left: 0; 109 | } 110 | 111 | // Button input groups 112 | // ------------------------- 113 | .input-group-btn { 114 | position: relative; 115 | white-space: nowrap; 116 | 117 | // Negative margin to only have a 1px border between the two 118 | &:first-child > .btn { 119 | margin-right: -1px; 120 | } 121 | &:last-child > .btn { 122 | margin-left: -1px; 123 | } 124 | } 125 | .input-group-btn > .btn { 126 | position: relative; 127 | // Jankily prevent input button groups from wrapping 128 | + .btn { 129 | margin-left: -4px; 130 | } 131 | // Bring the "active" button to the front 132 | &:hover, 133 | &:active { 134 | z-index: 2; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /resources/static/less/jumbotron.less: -------------------------------------------------------------------------------- 1 | // 2 | // Jumbotron 3 | // -------------------------------------------------- 4 | 5 | 6 | .jumbotron { 7 | padding: @jumbotron-padding; 8 | margin-bottom: @jumbotron-padding; 9 | font-size: @jumbotron-font-size; 10 | font-weight: 200; 11 | line-height: (@line-height-base * 1.5); 12 | color: @jumbotron-color; 13 | background-color: @jumbotron-bg; 14 | 15 | h1 { 16 | line-height: 1; 17 | color: @jumbotron-heading-color; 18 | } 19 | p { 20 | line-height: 1.4; 21 | } 22 | 23 | .container & { 24 | border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container 25 | } 26 | 27 | @media screen and (min-width: @screen-sm-min) { 28 | padding-top: (@jumbotron-padding * 1.6); 29 | padding-bottom: (@jumbotron-padding * 1.6); 30 | 31 | .container & { 32 | padding-left: (@jumbotron-padding * 2); 33 | padding-right: (@jumbotron-padding * 2); 34 | } 35 | 36 | h1 { 37 | font-size: (@font-size-base * 4.5); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /resources/static/less/labels.less: -------------------------------------------------------------------------------- 1 | // 2 | // Labels 3 | // -------------------------------------------------- 4 | 5 | .label { 6 | display: inline; 7 | padding: .2em .6em .3em; 8 | font-size: 75%; 9 | font-weight: bold; 10 | line-height: 1; 11 | color: @label-color; 12 | text-align: center; 13 | white-space: nowrap; 14 | vertical-align: baseline; 15 | border-radius: .25em; 16 | 17 | // Add hover effects, but only for links 18 | &[href] { 19 | &:hover, 20 | &:focus { 21 | color: @label-link-hover-color; 22 | text-decoration: none; 23 | cursor: pointer; 24 | } 25 | } 26 | 27 | // Empty labels collapse automatically (not available in IE8) 28 | &:empty { 29 | display: none; 30 | } 31 | } 32 | 33 | // Colors 34 | // Contextual variations (linked labels get darker on :hover) 35 | 36 | .label-default { 37 | .label-variant(@label-default-bg); 38 | } 39 | 40 | .label-primary { 41 | .label-variant(@label-primary-bg); 42 | } 43 | 44 | .label-success { 45 | .label-variant(@label-success-bg); 46 | } 47 | 48 | .label-info { 49 | .label-variant(@label-info-bg); 50 | } 51 | 52 | .label-warning { 53 | .label-variant(@label-warning-bg); 54 | } 55 | 56 | .label-danger { 57 | .label-variant(@label-danger-bg); 58 | } 59 | -------------------------------------------------------------------------------- /resources/static/less/list-group.less: -------------------------------------------------------------------------------- 1 | // 2 | // List groups 3 | // -------------------------------------------------- 4 | 5 | // Base class 6 | // 7 | // Easily usable on
    ,
      , or
      . 8 | .list-group { 9 | // No need to set list-style: none; since .list-group-item is block level 10 | margin-bottom: 20px; 11 | padding-left: 0; // reset padding because ul and ol 12 | } 13 | 14 | // Individual list items 15 | // ------------------------- 16 | 17 | .list-group-item { 18 | position: relative; 19 | display: block; 20 | padding: 10px 15px; 21 | // Place the border on the list items and negative margin up for better styling 22 | margin-bottom: -1px; 23 | background-color: @list-group-bg; 24 | border: 1px solid @list-group-border; 25 | 26 | // Round the first and last items 27 | &:first-child { 28 | .border-top-radius(@list-group-border-radius); 29 | } 30 | &:last-child { 31 | margin-bottom: 0; 32 | .border-bottom-radius(@list-group-border-radius); 33 | } 34 | 35 | // Align badges within list items 36 | > .badge { 37 | float: right; 38 | } 39 | > .badge + .badge { 40 | margin-right: 5px; 41 | } 42 | } 43 | 44 | // Linked list items 45 | a.list-group-item { 46 | color: @list-group-link-color; 47 | 48 | .list-group-item-heading { 49 | color: @list-group-link-heading-color; 50 | } 51 | 52 | // Hover state 53 | &:hover, 54 | &:focus { 55 | text-decoration: none; 56 | background-color: @list-group-hover-bg; 57 | } 58 | 59 | // Active class on item itself, not parent 60 | &.active, 61 | &.active:hover, 62 | &.active:focus { 63 | z-index: 2; // Place active items above their siblings for proper border styling 64 | color: @list-group-active-color; 65 | background-color: @list-group-active-bg; 66 | border-color: @list-group-active-border; 67 | 68 | // Force color to inherit for custom content 69 | .list-group-item-heading { 70 | color: inherit; 71 | } 72 | .list-group-item-text { 73 | color: lighten(@list-group-active-bg, 40%); 74 | } 75 | } 76 | } 77 | 78 | // Custom content options 79 | // ------------------------- 80 | 81 | .list-group-item-heading { 82 | margin-top: 0; 83 | margin-bottom: 5px; 84 | } 85 | .list-group-item-text { 86 | margin-bottom: 0; 87 | line-height: 1.3; 88 | } 89 | -------------------------------------------------------------------------------- /resources/static/less/media.less: -------------------------------------------------------------------------------- 1 | // Media objects 2 | // Source: http://stubbornella.org/content/?p=497 3 | // -------------------------------------------------- 4 | 5 | 6 | // Common styles 7 | // ------------------------- 8 | 9 | // Clear the floats 10 | .media, 11 | .media-body { 12 | overflow: hidden; 13 | zoom: 1; 14 | } 15 | 16 | // Proper spacing between instances of .media 17 | .media, 18 | .media .media { 19 | margin-top: 15px; 20 | } 21 | .media:first-child { 22 | margin-top: 0; 23 | } 24 | 25 | // For images and videos, set to block 26 | .media-object { 27 | display: block; 28 | } 29 | 30 | // Reset margins on headings for tighter default spacing 31 | .media-heading { 32 | margin: 0 0 5px; 33 | } 34 | 35 | 36 | // Media image alignment 37 | // ------------------------- 38 | 39 | .media { 40 | > .pull-left { 41 | margin-right: 10px; 42 | } 43 | > .pull-right { 44 | margin-left: 10px; 45 | } 46 | } 47 | 48 | 49 | // Media list variation 50 | // ------------------------- 51 | 52 | // Undo default ul/ol styles 53 | .media-list { 54 | padding-left: 0; 55 | list-style: none; 56 | } 57 | -------------------------------------------------------------------------------- /resources/static/less/modals.less: -------------------------------------------------------------------------------- 1 | // 2 | // Modals 3 | // -------------------------------------------------- 4 | 5 | // .modal-open - body class for killing the scroll 6 | // .modal - container to scroll within 7 | // .modal-dialog - positioning shell for the actual modal 8 | // .modal-content - actual modal w/ bg and corners and shit 9 | 10 | // Kill the scroll on the body 11 | .modal-open { 12 | overflow: hidden; 13 | } 14 | 15 | // Container that the modal scrolls within 16 | .modal { 17 | display: none; 18 | overflow: auto; 19 | overflow-y: scroll; 20 | position: fixed; 21 | top: 0; 22 | right: 0; 23 | bottom: 0; 24 | left: 0; 25 | z-index: @zindex-modal-background; 26 | 27 | // When fading in the modal, animate it to slide down 28 | &.fade .modal-dialog { 29 | .translate(0, -25%); 30 | .transition-transform(~"0.3s ease-out"); 31 | } 32 | &.in .modal-dialog { .translate(0, 0)} 33 | } 34 | 35 | // Shell div to position the modal with bottom padding 36 | .modal-dialog { 37 | position: relative; 38 | margin-left: auto; 39 | margin-right: auto; 40 | width: auto; 41 | padding: 10px; 42 | z-index: (@zindex-modal-background + 10); 43 | } 44 | 45 | // Actual modal 46 | .modal-content { 47 | position: relative; 48 | background-color: @modal-content-bg; 49 | border: 1px solid @modal-content-fallback-border-color; //old browsers fallback (ie8 etc) 50 | border: 1px solid @modal-content-border-color; 51 | border-radius: @border-radius-large; 52 | .box-shadow(0 3px 9px rgba(0,0,0,.5)); 53 | background-clip: padding-box; 54 | // Remove focus outline from opened modal 55 | outline: none; 56 | } 57 | 58 | // Modal background 59 | .modal-backdrop { 60 | position: fixed; 61 | top: 0; 62 | right: 0; 63 | bottom: 0; 64 | left: 0; 65 | z-index: (@zindex-modal-background - 10); 66 | background-color: @modal-backdrop-bg; 67 | // Fade for backdrop 68 | &.fade { .opacity(0); } 69 | &.in { .opacity(.5); } 70 | } 71 | 72 | // Modal header 73 | // Top section of the modal w/ title and dismiss 74 | .modal-header { 75 | padding: @modal-title-padding; 76 | border-bottom: 1px solid @modal-header-border-color; 77 | min-height: (@modal-title-padding + @modal-title-line-height); 78 | } 79 | // Close icon 80 | .modal-header .close { 81 | margin-top: -2px; 82 | } 83 | 84 | // Title text within header 85 | .modal-title { 86 | margin: 0; 87 | line-height: @modal-title-line-height; 88 | } 89 | 90 | // Modal body 91 | // Where all modal content resides (sibling of .modal-header and .modal-footer) 92 | .modal-body { 93 | position: relative; 94 | padding: @modal-inner-padding; 95 | } 96 | 97 | // Footer (for actions) 98 | .modal-footer { 99 | margin-top: 15px; 100 | padding: (@modal-inner-padding - 1) @modal-inner-padding @modal-inner-padding; 101 | text-align: right; // right align buttons 102 | border-top: 1px solid @modal-footer-border-color; 103 | .clearfix(); // clear it in case folks use .pull-* classes on buttons 104 | 105 | // Properly space out buttons 106 | .btn + .btn { 107 | margin-left: 5px; 108 | margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs 109 | } 110 | // but override that for button groups 111 | .btn-group .btn + .btn { 112 | margin-left: -1px; 113 | } 114 | // and override it for block buttons as well 115 | .btn-block + .btn-block { 116 | margin-left: 0; 117 | } 118 | } 119 | 120 | // Scale up the modal 121 | @media screen and (min-width: @screen-sm-min) { 122 | 123 | .modal-dialog { 124 | width: 600px; 125 | padding-top: 30px; 126 | padding-bottom: 30px; 127 | } 128 | .modal-content { 129 | .box-shadow(0 5px 15px rgba(0,0,0,.5)); 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /resources/static/less/pager.less: -------------------------------------------------------------------------------- 1 | // 2 | // Pager pagination 3 | // -------------------------------------------------- 4 | 5 | 6 | .pager { 7 | padding-left: 0; 8 | margin: @line-height-computed 0; 9 | list-style: none; 10 | text-align: center; 11 | .clearfix(); 12 | li { 13 | display: inline; 14 | > a, 15 | > span { 16 | display: inline-block; 17 | padding: 5px 14px; 18 | background-color: @pagination-bg; 19 | border: 1px solid @pagination-border; 20 | border-radius: @pager-border-radius; 21 | } 22 | 23 | > a:hover, 24 | > a:focus { 25 | text-decoration: none; 26 | background-color: @pagination-hover-bg; 27 | } 28 | } 29 | 30 | .next { 31 | > a, 32 | > span { 33 | float: right; 34 | } 35 | } 36 | 37 | .previous { 38 | > a, 39 | > span { 40 | float: left; 41 | } 42 | } 43 | 44 | .disabled { 45 | > a, 46 | > a:hover, 47 | > a:focus, 48 | > span { 49 | color: @pager-disabled-color; 50 | background-color: @pagination-bg; 51 | cursor: not-allowed; 52 | } 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /resources/static/less/pagination.less: -------------------------------------------------------------------------------- 1 | // 2 | // Pagination (multiple pages) 3 | // -------------------------------------------------- 4 | .pagination { 5 | display: inline-block; 6 | padding-left: 0; 7 | margin: @line-height-computed 0; 8 | border-radius: @border-radius-base; 9 | 10 | > li { 11 | display: inline; // Remove list-style and block-level defaults 12 | > a, 13 | > span { 14 | position: relative; 15 | float: left; // Collapse white-space 16 | padding: @padding-base-vertical @padding-base-horizontal; 17 | line-height: @line-height-base; 18 | text-decoration: none; 19 | background-color: @pagination-bg; 20 | border: 1px solid @pagination-border; 21 | margin-left: -1px; 22 | } 23 | &:first-child { 24 | > a, 25 | > span { 26 | margin-left: 0; 27 | .border-left-radius(@border-radius-base); 28 | } 29 | } 30 | &:last-child { 31 | > a, 32 | > span { 33 | .border-right-radius(@border-radius-base); 34 | } 35 | } 36 | } 37 | 38 | > li > a, 39 | > li > span { 40 | &:hover, 41 | &:focus { 42 | background-color: @pagination-hover-bg; 43 | } 44 | } 45 | 46 | > .active > a, 47 | > .active > span { 48 | &, 49 | &:hover, 50 | &:focus { 51 | z-index: 2; 52 | color: @pagination-active-color; 53 | background-color: @pagination-active-bg; 54 | border-color: @pagination-active-bg; 55 | cursor: default; 56 | } 57 | } 58 | 59 | > .disabled { 60 | > span, 61 | > span:hover, 62 | > span:focus, 63 | > a, 64 | > a:hover, 65 | > a:focus { 66 | color: @pagination-disabled-color; 67 | background-color: @pagination-bg; 68 | border-color: @pagination-border; 69 | cursor: not-allowed; 70 | } 71 | } 72 | } 73 | 74 | // Sizing 75 | // -------------------------------------------------- 76 | 77 | // Large 78 | .pagination-lg { 79 | .pagination-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @border-radius-large); 80 | } 81 | 82 | // Small 83 | .pagination-sm { 84 | .pagination-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @border-radius-small); 85 | } 86 | -------------------------------------------------------------------------------- /resources/static/less/popovers.less: -------------------------------------------------------------------------------- 1 | // 2 | // Popovers 3 | // -------------------------------------------------- 4 | 5 | 6 | .popover { 7 | position: absolute; 8 | top: 0; 9 | left: 0; 10 | z-index: @zindex-popover; 11 | display: none; 12 | max-width: @popover-max-width; 13 | padding: 1px; 14 | text-align: left; // Reset given new insertion method 15 | background-color: @popover-bg; 16 | background-clip: padding-box; 17 | border: 1px solid @popover-fallback-border-color; 18 | border: 1px solid @popover-border-color; 19 | border-radius: @border-radius-large; 20 | .box-shadow(0 5px 10px rgba(0,0,0,.2)); 21 | 22 | // Overrides for proper insertion 23 | white-space: normal; 24 | 25 | // Offset the popover to account for the popover arrow 26 | &.top { margin-top: -10px; } 27 | &.right { margin-left: 10px; } 28 | &.bottom { margin-top: 10px; } 29 | &.left { margin-left: -10px; } 30 | } 31 | 32 | .popover-title { 33 | margin: 0; // reset heading margin 34 | padding: 8px 14px; 35 | font-size: @font-size-base; 36 | font-weight: normal; 37 | line-height: 18px; 38 | background-color: @popover-title-bg; 39 | border-bottom: 1px solid darken(@popover-title-bg, 5%); 40 | border-radius: 5px 5px 0 0; 41 | } 42 | 43 | .popover-content { 44 | padding: 9px 14px; 45 | } 46 | 47 | // Arrows 48 | // 49 | // .arrow is outer, .arrow:after is inner 50 | 51 | .popover .arrow { 52 | &, 53 | &:after { 54 | position: absolute; 55 | display: block; 56 | width: 0; 57 | height: 0; 58 | border-color: transparent; 59 | border-style: solid; 60 | } 61 | } 62 | .popover .arrow { 63 | border-width: @popover-arrow-outer-width; 64 | } 65 | .popover .arrow:after { 66 | border-width: @popover-arrow-width; 67 | content: ""; 68 | } 69 | 70 | .popover { 71 | &.top .arrow { 72 | left: 50%; 73 | margin-left: -@popover-arrow-outer-width; 74 | border-bottom-width: 0; 75 | border-top-color: @popover-arrow-outer-fallback-color; // IE8 fallback 76 | border-top-color: @popover-arrow-outer-color; 77 | bottom: -@popover-arrow-outer-width; 78 | &:after { 79 | content: " "; 80 | bottom: 1px; 81 | margin-left: -@popover-arrow-width; 82 | border-bottom-width: 0; 83 | border-top-color: @popover-arrow-color; 84 | } 85 | } 86 | &.right .arrow { 87 | top: 50%; 88 | left: -@popover-arrow-outer-width; 89 | margin-top: -@popover-arrow-outer-width; 90 | border-left-width: 0; 91 | border-right-color: @popover-arrow-outer-fallback-color; // IE8 fallback 92 | border-right-color: @popover-arrow-outer-color; 93 | &:after { 94 | content: " "; 95 | left: 1px; 96 | bottom: -@popover-arrow-width; 97 | border-left-width: 0; 98 | border-right-color: @popover-arrow-color; 99 | } 100 | } 101 | &.bottom .arrow { 102 | left: 50%; 103 | margin-left: -@popover-arrow-outer-width; 104 | border-top-width: 0; 105 | border-bottom-color: @popover-arrow-outer-fallback-color; // IE8 fallback 106 | border-bottom-color: @popover-arrow-outer-color; 107 | top: -@popover-arrow-outer-width; 108 | &:after { 109 | content: " "; 110 | top: 1px; 111 | margin-left: -@popover-arrow-width; 112 | border-top-width: 0; 113 | border-bottom-color: @popover-arrow-color; 114 | } 115 | } 116 | 117 | &.left .arrow { 118 | top: 50%; 119 | right: -@popover-arrow-outer-width; 120 | margin-top: -@popover-arrow-outer-width; 121 | border-right-width: 0; 122 | border-left-color: @popover-arrow-outer-fallback-color; // IE8 fallback 123 | border-left-color: @popover-arrow-outer-color; 124 | &:after { 125 | content: " "; 126 | right: 1px; 127 | border-right-width: 0; 128 | border-left-color: @popover-arrow-color; 129 | bottom: -@popover-arrow-width; 130 | } 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /resources/static/less/print.less: -------------------------------------------------------------------------------- 1 | // 2 | // Basic print styles 3 | // -------------------------------------------------- 4 | // Source: https://github.com/h5bp/html5-boilerplate/blob/master/css/main.css 5 | 6 | @media print { 7 | 8 | * { 9 | text-shadow: none !important; 10 | color: #000 !important; // Black prints faster: h5bp.com/s 11 | background: transparent !important; 12 | box-shadow: none !important; 13 | } 14 | 15 | a, 16 | a:visited { 17 | text-decoration: underline; 18 | } 19 | 20 | a[href]:after { 21 | content: " (" attr(href) ")"; 22 | } 23 | 24 | abbr[title]:after { 25 | content: " (" attr(title) ")"; 26 | } 27 | 28 | // Don't show links for images, or javascript/internal links 29 | a[href^="javascript:"]:after, 30 | a[href^="#"]:after { 31 | content: ""; 32 | } 33 | 34 | pre, 35 | blockquote { 36 | border: 1px solid #999; 37 | page-break-inside: avoid; 38 | } 39 | 40 | thead { 41 | display: table-header-group; // h5bp.com/t 42 | } 43 | 44 | tr, 45 | img { 46 | page-break-inside: avoid; 47 | } 48 | 49 | img { 50 | max-width: 100% !important; 51 | } 52 | 53 | @page { 54 | margin: 2cm .5cm; 55 | } 56 | 57 | p, 58 | h2, 59 | h3 { 60 | orphans: 3; 61 | widows: 3; 62 | } 63 | 64 | h2, 65 | h3 { 66 | page-break-after: avoid; 67 | } 68 | 69 | // Chrome (OSX) fix for https://github.com/twbs/bootstrap/issues/11245 70 | // Once fixed, we can just straight up remove this. 71 | select { 72 | background: #fff !important; 73 | } 74 | 75 | // Bootstrap components 76 | .navbar { 77 | display: none; 78 | } 79 | .table { 80 | td, 81 | th { 82 | background-color: #fff !important; 83 | } 84 | } 85 | .btn, 86 | .dropup > .btn { 87 | > .caret { 88 | border-top-color: #000 !important; 89 | } 90 | } 91 | .label { 92 | border: 1px solid #000; 93 | } 94 | 95 | .table { 96 | border-collapse: collapse !important; 97 | } 98 | .table-bordered { 99 | th, 100 | td { 101 | border: 1px solid #ddd !important; 102 | } 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /resources/static/less/progress-bars.less: -------------------------------------------------------------------------------- 1 | // 2 | // Progress bars 3 | // -------------------------------------------------- 4 | 5 | 6 | // Bar animations 7 | // ------------------------- 8 | 9 | // WebKit 10 | @-webkit-keyframes progress-bar-stripes { 11 | from { background-position: 40px 0; } 12 | to { background-position: 0 0; } 13 | } 14 | 15 | // Firefox 16 | @-moz-keyframes progress-bar-stripes { 17 | from { background-position: 40px 0; } 18 | to { background-position: 0 0; } 19 | } 20 | 21 | // Opera 22 | @-o-keyframes progress-bar-stripes { 23 | from { background-position: 0 0; } 24 | to { background-position: 40px 0; } 25 | } 26 | 27 | // Spec and IE10+ 28 | @keyframes progress-bar-stripes { 29 | from { background-position: 40px 0; } 30 | to { background-position: 0 0; } 31 | } 32 | 33 | 34 | 35 | // Bar itself 36 | // ------------------------- 37 | 38 | // Outer container 39 | .progress { 40 | overflow: hidden; 41 | height: @line-height-computed; 42 | margin-bottom: @line-height-computed; 43 | background-color: @progress-bg; 44 | border-radius: @border-radius-base; 45 | .box-shadow(inset 0 1px 2px rgba(0,0,0,.1)); 46 | } 47 | 48 | // Bar of progress 49 | .progress-bar { 50 | float: left; 51 | width: 0%; 52 | height: 100%; 53 | font-size: @font-size-small; 54 | line-height: @line-height-computed; 55 | color: @progress-bar-color; 56 | text-align: center; 57 | background-color: @progress-bar-bg; 58 | .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15)); 59 | .transition(width .6s ease); 60 | } 61 | 62 | // Striped bars 63 | .progress-striped .progress-bar { 64 | #gradient > .striped(); 65 | background-size: 40px 40px; 66 | } 67 | 68 | // Call animation for the active one 69 | .progress.active .progress-bar { 70 | .animation(progress-bar-stripes 2s linear infinite); 71 | } 72 | 73 | 74 | 75 | // Variations 76 | // ------------------------- 77 | 78 | .progress-bar-success { 79 | .progress-bar-variant(@progress-bar-success-bg); 80 | } 81 | 82 | .progress-bar-info { 83 | .progress-bar-variant(@progress-bar-info-bg); 84 | } 85 | 86 | .progress-bar-warning { 87 | .progress-bar-variant(@progress-bar-warning-bg); 88 | } 89 | 90 | .progress-bar-danger { 91 | .progress-bar-variant(@progress-bar-danger-bg); 92 | } 93 | -------------------------------------------------------------------------------- /resources/static/less/scaffolding.less: -------------------------------------------------------------------------------- 1 | // 2 | // Scaffolding 3 | // -------------------------------------------------- 4 | 5 | 6 | // Reset the box-sizing 7 | 8 | *, 9 | *:before, 10 | *:after { 11 | .box-sizing(border-box); 12 | } 13 | 14 | 15 | // Body reset 16 | 17 | html { 18 | font-size: 62.5%; 19 | -webkit-tap-highlight-color: rgba(0,0,0,0); 20 | } 21 | 22 | body { 23 | font-family: @font-family-base; 24 | font-size: @font-size-base; 25 | line-height: @line-height-base; 26 | color: @text-color; 27 | background-color: @body-bg; 28 | } 29 | 30 | // Reset fonts for relevant elements 31 | input, 32 | button, 33 | select, 34 | textarea { 35 | font-family: inherit; 36 | font-size: inherit; 37 | line-height: inherit; 38 | } 39 | 40 | 41 | // Links 42 | 43 | a { 44 | color: @link-color; 45 | text-decoration: none; 46 | 47 | &:hover, 48 | &:focus { 49 | color: @link-hover-color; 50 | text-decoration: underline; 51 | } 52 | 53 | &:focus { 54 | .tab-focus(); 55 | } 56 | } 57 | 58 | 59 | // Images 60 | 61 | img { 62 | vertical-align: middle; 63 | } 64 | 65 | // Responsive images (ensure images don't scale beyond their parents) 66 | .img-responsive { 67 | .img-responsive(); 68 | } 69 | 70 | // Rounded corners 71 | .img-rounded { 72 | border-radius: @border-radius-large; 73 | } 74 | 75 | // Image thumbnails 76 | // 77 | // Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`. 78 | .img-thumbnail { 79 | padding: @thumbnail-padding; 80 | line-height: @line-height-base; 81 | background-color: @thumbnail-bg; 82 | border: 1px solid @thumbnail-border; 83 | border-radius: @thumbnail-border-radius; 84 | .transition(all .2s ease-in-out); 85 | 86 | // Keep them at most 100% wide 87 | .img-responsive(inline-block); 88 | } 89 | 90 | // Perfect circle 91 | .img-circle { 92 | border-radius: 50%; // set radius in percents 93 | } 94 | 95 | 96 | // Horizontal rules 97 | 98 | hr { 99 | margin-top: @line-height-computed; 100 | margin-bottom: @line-height-computed; 101 | border: 0; 102 | border-top: 1px solid @hr-border; 103 | } 104 | 105 | 106 | // Only display content to screen readers 107 | // 108 | // See: http://a11yproject.com/posts/how-to-hide-content/ 109 | 110 | .sr-only { 111 | position: absolute; 112 | width: 1px; 113 | height: 1px; 114 | margin: -1px; 115 | padding: 0; 116 | overflow: hidden; 117 | clip: rect(0,0,0,0); 118 | border: 0; 119 | } 120 | -------------------------------------------------------------------------------- /resources/static/less/thumbnails.less: -------------------------------------------------------------------------------- 1 | // 2 | // Thumbnails 3 | // -------------------------------------------------- 4 | 5 | 6 | // Mixin and adjust the regular image class 7 | .thumbnail { 8 | .img-thumbnail(); 9 | display: block; // Override the inline-block from `.img-thumbnail` 10 | margin-bottom: @line-height-computed; 11 | 12 | > img { 13 | .img-responsive(); 14 | margin-left: auto; 15 | margin-right: auto; 16 | } 17 | } 18 | 19 | 20 | // Add a hover state for linked versions only 21 | a.thumbnail:hover, 22 | a.thumbnail:focus, 23 | a.thumbnail.active { 24 | border-color: @link-color; 25 | } 26 | 27 | // Image captions 28 | .thumbnail .caption { 29 | padding: @thumbnail-caption-padding; 30 | color: @thumbnail-caption-color; 31 | } 32 | -------------------------------------------------------------------------------- /resources/static/less/tooltip.less: -------------------------------------------------------------------------------- 1 | // 2 | // Tooltips 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .tooltip { 8 | position: absolute; 9 | z-index: @zindex-tooltip; 10 | display: block; 11 | visibility: visible; 12 | font-size: @font-size-small; 13 | line-height: 1.4; 14 | .opacity(0); 15 | 16 | &.in { .opacity(.9); } 17 | &.top { margin-top: -3px; padding: @tooltip-arrow-width 0; } 18 | &.right { margin-left: 3px; padding: 0 @tooltip-arrow-width; } 19 | &.bottom { margin-top: 3px; padding: @tooltip-arrow-width 0; } 20 | &.left { margin-left: -3px; padding: 0 @tooltip-arrow-width; } 21 | } 22 | 23 | // Wrapper for the tooltip content 24 | .tooltip-inner { 25 | max-width: @tooltip-max-width; 26 | padding: 3px 8px; 27 | color: @tooltip-color; 28 | text-align: center; 29 | text-decoration: none; 30 | background-color: @tooltip-bg; 31 | border-radius: @border-radius-base; 32 | } 33 | 34 | // Arrows 35 | .tooltip-arrow { 36 | position: absolute; 37 | width: 0; 38 | height: 0; 39 | border-color: transparent; 40 | border-style: solid; 41 | } 42 | .tooltip { 43 | &.top .tooltip-arrow { 44 | bottom: 0; 45 | left: 50%; 46 | margin-left: -@tooltip-arrow-width; 47 | border-width: @tooltip-arrow-width @tooltip-arrow-width 0; 48 | border-top-color: @tooltip-arrow-color; 49 | } 50 | &.top-left .tooltip-arrow { 51 | bottom: 0; 52 | left: @tooltip-arrow-width; 53 | border-width: @tooltip-arrow-width @tooltip-arrow-width 0; 54 | border-top-color: @tooltip-arrow-color; 55 | } 56 | &.top-right .tooltip-arrow { 57 | bottom: 0; 58 | right: @tooltip-arrow-width; 59 | border-width: @tooltip-arrow-width @tooltip-arrow-width 0; 60 | border-top-color: @tooltip-arrow-color; 61 | } 62 | &.right .tooltip-arrow { 63 | top: 50%; 64 | left: 0; 65 | margin-top: -@tooltip-arrow-width; 66 | border-width: @tooltip-arrow-width @tooltip-arrow-width @tooltip-arrow-width 0; 67 | border-right-color: @tooltip-arrow-color; 68 | } 69 | &.left .tooltip-arrow { 70 | top: 50%; 71 | right: 0; 72 | margin-top: -@tooltip-arrow-width; 73 | border-width: @tooltip-arrow-width 0 @tooltip-arrow-width @tooltip-arrow-width; 74 | border-left-color: @tooltip-arrow-color; 75 | } 76 | &.bottom .tooltip-arrow { 77 | top: 0; 78 | left: 50%; 79 | margin-left: -@tooltip-arrow-width; 80 | border-width: 0 @tooltip-arrow-width @tooltip-arrow-width; 81 | border-bottom-color: @tooltip-arrow-color; 82 | } 83 | &.bottom-left .tooltip-arrow { 84 | top: 0; 85 | left: @tooltip-arrow-width; 86 | border-width: 0 @tooltip-arrow-width @tooltip-arrow-width; 87 | border-bottom-color: @tooltip-arrow-color; 88 | } 89 | &.bottom-right .tooltip-arrow { 90 | top: 0; 91 | right: @tooltip-arrow-width; 92 | border-width: 0 @tooltip-arrow-width @tooltip-arrow-width; 93 | border-bottom-color: @tooltip-arrow-color; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /resources/static/less/utilities.less: -------------------------------------------------------------------------------- 1 | // 2 | // Utility classes 3 | // -------------------------------------------------- 4 | 5 | 6 | // Floats 7 | // ------------------------- 8 | 9 | .clearfix { 10 | .clearfix(); 11 | } 12 | .center-block { 13 | .center-block(); 14 | } 15 | .pull-right { 16 | float: right !important; 17 | } 18 | .pull-left { 19 | float: left !important; 20 | } 21 | 22 | 23 | // Toggling content 24 | // ------------------------- 25 | 26 | // Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1 27 | .hide { 28 | display: none !important; 29 | } 30 | .show { 31 | display: block !important; 32 | } 33 | .invisible { 34 | visibility: hidden; 35 | } 36 | .text-hide { 37 | .text-hide(); 38 | } 39 | 40 | 41 | // Hide from screenreaders and browsers 42 | // 43 | // Credit: HTML5 Boilerplate 44 | 45 | .hidden { 46 | display: none !important; 47 | visibility: hidden !important; 48 | } 49 | 50 | 51 | // For Affix plugin 52 | // ------------------------- 53 | 54 | .affix { 55 | position: fixed; 56 | } 57 | -------------------------------------------------------------------------------- /resources/static/less/wells.less: -------------------------------------------------------------------------------- 1 | // 2 | // Wells 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .well { 8 | min-height: 20px; 9 | padding: 19px; 10 | margin-bottom: 20px; 11 | background-color: @well-bg; 12 | border: 1px solid darken(@well-bg, 7%); 13 | border-radius: @border-radius-base; 14 | .box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); 15 | blockquote { 16 | border-color: #ddd; 17 | border-color: rgba(0,0,0,.15); 18 | } 19 | } 20 | 21 | // Sizes 22 | .well-lg { 23 | padding: 24px; 24 | border-radius: @border-radius-large; 25 | } 26 | .well-sm { 27 | padding: 9px; 28 | border-radius: @border-radius-small; 29 | } 30 | -------------------------------------------------------------------------------- /resources/templates/404.tmpl: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | <%inherit file="navbar.tmpl"/> 3 | <%block name="core"> 4 | 7 | 8 | -------------------------------------------------------------------------------- /resources/templates/adduser.tmpl: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | <%inherit file="navbar.tmpl"/> 3 | <%block name="core"> 4 |
      5 |
      6 |
      7 |
      8 |
      9 | 25 | 26 |
      27 |
      28 |
      29 |
      30 |
      31 | 32 | -------------------------------------------------------------------------------- /resources/templates/base.tmpl: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | 3 | 4 | 5 | 6 | LdapCherry 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | % if custom_js: 46 | % for js in custom_js: 47 | 48 | % endfor 49 | %endif 50 | 51 | 56 | 57 | 58 | 59 | % if notifications: 60 | % for notif in notifications: 61 | 62 | % endfor 63 | % endif 64 |
      65 | <%block name="navbar"/> 66 | <%block name="core" /> 67 |
      68 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /resources/templates/error.tmpl: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | <%inherit file="navbar.tmpl"/> 3 | <%block name="core"> 4 |
      5 |
      6 |
      7 | 8 |

      9 | ${message} 10 |

      11 |
      12 | Return 13 |
      14 |
      15 | 16 | 17 | -------------------------------------------------------------------------------- /resources/templates/groups.tmpl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakwa/ldapcherry/662e09eccf7ff9cf697e92d8a3950927014ae0b1/resources/templates/groups.tmpl -------------------------------------------------------------------------------- /resources/templates/index.tmpl: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | <%inherit file="navbar.tmpl"/> 3 | <%block name="core"> 4 |
      5 |
      6 |
      7 |
      8 |
      9 |
      10 |

      Your attributes:

      11 |
      12 |
      13 | 14 | % if not searchresult is None: 15 | 16 | %for attr in sorted(attrs_list.keys(), key=lambda attr: attrs_list[attr]['weight']): 17 | 18 | % if attr in searchresult: 19 | <% 20 | value = searchresult[attr] 21 | if type(value) is list: 22 | value = ', '.join(value) 23 | %> 24 | 25 | 26 | % endif 27 | 28 | % endfor 29 | 30 | %endif 31 |
      ${attrs_list[attr]['display_name']}:${value}
      32 |
      33 |
      34 |
      35 |
      36 |
      37 |
      38 | 39 | -------------------------------------------------------------------------------- /resources/templates/login.tmpl: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | <%inherit file="base.tmpl"/> 3 | <%block name="core"> 4 |
      5 |
      6 |
      7 | 33 |
      34 |
      35 |
      36 | 37 | -------------------------------------------------------------------------------- /resources/templates/modify.tmpl: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | <%inherit file="navbar.tmpl"/> 3 | <%block name="core"> 4 |
      5 |
      6 |
      7 |
      8 |
      9 | 65 | 66 |
      67 |
      68 |
      69 |
      70 |
      71 | 72 | -------------------------------------------------------------------------------- /resources/templates/navbar.tmpl: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | #<%inherit file="base.tmpl"/> 3 | <%block name="navbar"> 4 |
      5 |
      6 | 34 |
      35 |
      36 | 37 | -------------------------------------------------------------------------------- /resources/templates/searchadmin.tmpl: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | <%inherit file="navbar.tmpl"/> 3 | <%block name="core"> 4 |
      5 |
      6 |
      7 |
      8 | 9 | 10 |
      11 |
      12 | 13 | 15 |
      16 |
      17 |
      18 |
      19 | % if not searchresult is None: 20 |
      21 |
      22 |
      23 | 24 | 25 | 26 | %for attr in sorted(attrs_list.keys(), key=lambda attr: attrs_list[attr]['weight']): 27 | 30 | % endfor 31 | 34 | 37 | 38 | 39 | 40 | %for user in searchresult: 41 | 42 | %for attr in sorted(attrs_list.keys(), key=lambda attr: attrs_list[attr]['weight']): 43 | 53 | % endfor 54 | 57 | 60 | 61 | % endfor 62 | 63 |
      28 | ${attrs_list[attr]['display_name']} 29 | 32 | Modify 33 | 35 | Delete 36 |
      44 | % if attr in searchresult[user]: 45 | <% 46 | value = searchresult[user][attr] 47 | if type(value) is list: 48 | value = ', '.join(value) 49 | %> 50 | ${value} 51 | % endif 52 | 55 | Modify 56 | 58 | Delete 59 |
      64 |
      65 |
      66 |
      67 | %endif 68 | 75 | 76 | -------------------------------------------------------------------------------- /resources/templates/searchuser.tmpl: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | <%inherit file="navbar.tmpl"/> 3 | <%block name="core"> 4 |
      5 |
      6 |
      7 |
      8 | 9 | 10 |
      11 |
      12 | 13 | 15 |
      16 |
      17 |
      18 |
      19 | % if not searchresult is None: 20 |
      21 |
      22 |
      23 | 24 | 25 | 26 | %for attr in sorted(attrs_list.keys(), key=lambda attr: attrs_list[attr]['weight']): 27 | 30 | % endfor 31 | 32 | 33 | 34 | %for user in searchresult: 35 | 36 | %for attr in sorted(attrs_list.keys(), key=lambda attr: attrs_list[attr]['weight']): 37 | 47 | % endfor 48 | 49 | % endfor 50 | 51 |
      28 | ${attrs_list[attr]['display_name']} 29 |
      38 | % if attr in searchresult[user]: 39 | <% 40 | value = searchresult[user][attr] 41 | if type(value) is list: 42 | value = ', '.join(value) 43 | %> 44 | ${value} 45 | % endif 46 |
      52 |
      53 |
      54 |
      55 | %endif 56 | 57 | -------------------------------------------------------------------------------- /resources/templates/selfmodify.tmpl: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | <%inherit file="navbar.tmpl"/> 3 | <%block name="core"> 4 |
      5 |
      6 |
      7 |
      8 |
      9 | 20 | 21 |
      22 |
      23 |
      24 |
      25 |
      26 | 27 | -------------------------------------------------------------------------------- /resources/templates/service_unavailable.tmpl: -------------------------------------------------------------------------------- 1 | ## -*- coding: utf-8 -*- 2 | <%inherit file="base.tmpl"/> 3 | <%block name="core"> 4 |
      5 |
      6 |
      7 |
      8 |

      9 | Service Unavailable 10 |

      11 |
      12 | Return 13 |
      14 |
      15 |
      16 | 17 | -------------------------------------------------------------------------------- /run_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | Red='\33[0;31m'; 4 | Gre='\33[0;32m'; 5 | RCol='\33[0m'; 6 | 7 | cd `dirname $0` 8 | python3 setup.py test #&&\ 9 | #printf "\nPEP 8 compliance check:\n\n" 10 | #pep8 \ 11 | # --recurssive ./ \ 12 | # --show-source \ 13 | # --exclude=.venv,.tox,dist,docs,build,*.egg,tests,misc . && \ 14 | # printf "[${Gre}Passed${RCol}] Yeah! everything is clean\n\n" || \ 15 | # printf "[${Red}Failed${RCol}] Oh No! there is some mess to fix\n\n" 16 | -------------------------------------------------------------------------------- /tests/cfg/attribute_pwderror.yml: -------------------------------------------------------------------------------- 1 | uid: 2 | description: "UID of the user" 3 | display_name: "UID" 4 | search_displayed: True 5 | key: True 6 | type: string 7 | weight: 50 8 | autofill: 9 | function: uid 10 | args: 11 | - $first-name 12 | - $last-name 13 | backends: 14 | ldap: uid 15 | ad: UID 16 | password1: 17 | description: "Home user path" 18 | display_name: "Home" 19 | weight: 90 20 | type: string 21 | backends: 22 | ldap: home 23 | ad: Home 24 | password: 25 | description: "Password of the user" 26 | display_name: "Password" 27 | weight: 31 28 | self: True 29 | type: password 30 | backends: 31 | ldap: userPassword 32 | ad: unicodePwd 33 | cn: 34 | description: "First Name and Display Name" 35 | display_name: "Display Name" 36 | type: string 37 | weight: 30 38 | backends: 39 | ad: cn 40 | -------------------------------------------------------------------------------- /tests/cfg/attributes.yml: -------------------------------------------------------------------------------- 1 | cn: 2 | description: "Firt Name and Display Name" 3 | display_name: "Display Name" 4 | type: string 5 | search_displayed: True 6 | weight: 30 7 | autofill: 8 | function: cn 9 | args: 10 | - $first-name 11 | - $name 12 | backends: 13 | ldap: cn 14 | ad: cn 15 | 16 | first-name: 17 | description: "First name of the user" 18 | display_name: "First Name" 19 | search_displayed: True 20 | type: string 21 | weight: 20 22 | backends: 23 | ldap: givenName 24 | ad: givenName 25 | name: 26 | description: "Family name of the user" 27 | display_name: "Name" 28 | search_displayed: True 29 | weight: 10 30 | type: string 31 | backends: 32 | ldap: sn 33 | ad: sn 34 | email: 35 | description: "Email of the user" 36 | display_name: "Name" 37 | type: email 38 | weight: 40 39 | autofill: 40 | function: email 41 | args: 42 | - $first-name 43 | - $last-name 44 | - '@example.com' 45 | backends: 46 | ldap: email 47 | ad: EMAIL 48 | uid: 49 | description: "UID of the user" 50 | display_name: "UID" 51 | search_displayed: True 52 | key: True 53 | type: string 54 | weight: 50 55 | autofill: 56 | function: uid 57 | args: 58 | - $first-name 59 | - $last-name 60 | backends: 61 | ldap: uid 62 | ad: UID 63 | uidNumber: 64 | description: "User ID Number of the user" 65 | display_name: "UID Number" 66 | weight: 60 67 | type: int 68 | autofill: 69 | function: uidNumber 70 | args: 71 | - $first-name 72 | - $last-name 73 | backends: 74 | ldap: uidNumber 75 | ad: UIDNumber 76 | gidNumber: 77 | description: "Group ID Number of the user" 78 | display_name: "GID Number" 79 | weight: 70 80 | type: int 81 | default: 10000 82 | backends: 83 | ldap: gidNumber 84 | ad: gidNumber 85 | shell: 86 | description: "Shell of the user" 87 | display_name: "Shell" 88 | weight: 80 89 | self: True 90 | type: stringlist 91 | values: 92 | - /bin/bash 93 | - /bin/zsh 94 | - /bin/sh 95 | backends: 96 | ldap: shell 97 | ad: SHELL 98 | home: 99 | description: "Home user path" 100 | display_name: "Home" 101 | weight: 90 102 | type: string 103 | autofill: 104 | function: home 105 | args: 106 | - $first-name 107 | - $last-name 108 | - /home/ 109 | backends: 110 | ldap: home 111 | ad: Home 112 | password: 113 | description: "Password of the user" 114 | display_name: "Password" 115 | weight: 31 116 | self: True 117 | type: password 118 | backends: 119 | ldap: userPassword 120 | ad: unicodePwd 121 | logscript: 122 | description: "Windows login script" 123 | display_name: "Login script" 124 | weight: 100 125 | type: fix 126 | value: login1.bat 127 | backends: 128 | ad: logonScript 129 | -------------------------------------------------------------------------------- /tests/cfg/attributes_adldap.yml: -------------------------------------------------------------------------------- 1 | cn: 2 | description: "First Name and Display Name" 3 | display_name: "Display Name" 4 | type: string 5 | weight: 30 6 | autofill: 7 | function: lcDisplayName 8 | args: 9 | - $first-name 10 | - $name 11 | backends: 12 | ldap: cn 13 | ad: cn 14 | first-name: 15 | description: "First name of the user" 16 | display_name: "First Name" 17 | search_displayed: True 18 | type: string 19 | weight: 20 20 | backends: 21 | ldap: givenName 22 | ad: givenName 23 | name: 24 | description: "Family name of the user" 25 | display_name: "Name" 26 | search_displayed: True 27 | weight: 10 28 | type: string 29 | backends: 30 | ldap: sn 31 | ad: sn 32 | email: 33 | description: "Email of the user" 34 | display_name: "Email" 35 | search_displayed: True 36 | type: email 37 | weight: 40 38 | autofill: 39 | function: lcMail 40 | args: 41 | - $first-name 42 | - $name 43 | - '@example.com' 44 | backends: 45 | ldap: mail 46 | ad: mail 47 | uid: 48 | description: "UID of the user" 49 | display_name: "UID" 50 | search_displayed: True 51 | key: True 52 | type: string 53 | weight: 50 54 | autofill: 55 | function: lcUid 56 | args: 57 | - $first-name 58 | - $name 59 | backends: 60 | ldap: uid 61 | ad: sAMAccountName 62 | uidNumber: 63 | description: "User ID Number of the user" 64 | display_name: "UID Number" 65 | weight: 60 66 | type: int 67 | autofill: 68 | function: lcUidNumber 69 | args: 70 | - $first-name 71 | - $name 72 | - '10000' 73 | - '30000' 74 | backends: 75 | ldap: uidNumber 76 | ad: uidNumber 77 | gidNumber: 78 | description: "Group ID Number of the user" 79 | display_name: "GID Number" 80 | weight: 70 81 | type: int 82 | autofill: 83 | function: lcUidNumber 84 | args: 85 | - $first-name 86 | - $name 87 | - '10000' 88 | - '30000' 89 | backends: 90 | ldap: gidNumber 91 | ad: gidNumber 92 | shell: 93 | description: "Shell of the user" 94 | display_name: "Shell" 95 | weight: 80 96 | self: True 97 | type: stringlist 98 | values: 99 | - /bin/bash 100 | - /bin/zsh 101 | - /bin/sh 102 | backends: 103 | ldap: loginShell 104 | ad: loginShell 105 | home: 106 | description: "Home user path" 107 | display_name: "Home" 108 | weight: 90 109 | type: string 110 | autofill: 111 | function: lcHomeDir 112 | args: 113 | - $first-name 114 | - $name 115 | - /home/ 116 | backends: 117 | ldap: homeDirectory 118 | ad: homeDirectory 119 | password: 120 | description: "Password of the user" 121 | display_name: "Password" 122 | weight: 31 123 | self: True 124 | type: password 125 | backends: 126 | ldap: userPassword 127 | ad: unicodePwd 128 | 129 | logscript: 130 | description: "Windows login script" 131 | display_name: "Login script" 132 | weight: 100 133 | type: fix 134 | value: login1.bat 135 | backends: 136 | ad: scriptPath 137 | -------------------------------------------------------------------------------- /tests/cfg/attributes_missing_mandatory.yml: -------------------------------------------------------------------------------- 1 | cn: 2 | description: "Firt Name and Display Name" 3 | display_name: "Display Name" 4 | type: string 5 | autofill: 6 | function: cn 7 | args: 8 | - $first-name 9 | - $name 10 | backends: 11 | ldap: cn 12 | ad: cn 13 | first-name: 14 | description: "First name of the user" 15 | display_name: "First Name" 16 | type: string 17 | backends: 18 | ldap: givenName 19 | ad: givenName 20 | name: 21 | description: "Family name of the user" 22 | display_name: "Name" 23 | type: string 24 | backends: 25 | ldap: sn 26 | ad: sn 27 | email: 28 | description: "Email of the user" 29 | display_name: "Name" 30 | type: email 31 | autofill: 32 | function: email 33 | args: 34 | - $first-name 35 | - $last-name 36 | - '@example.com' 37 | backends: 38 | ldap: email 39 | ad: EMAIL 40 | uid: 41 | display_name: "UID" 42 | type: string 43 | key: True 44 | autofill: 45 | function: uid 46 | args: 47 | - $first-name 48 | - $last-name 49 | backends: 50 | ldap: uid 51 | ad: UID 52 | uidNumber: 53 | description: "User ID Number of the user" 54 | display_name: "UID Number" 55 | type: int 56 | autofill: 57 | function: uidNumber 58 | args: 59 | - $first-name 60 | - $last-name 61 | backends: 62 | ldap: uidNumber 63 | ad: UIDNumber 64 | gidNumber: 65 | description: "Group ID Number of the user" 66 | display_name: "GID Number" 67 | type: int 68 | default: 10000 69 | backends: 70 | ldap: gidNumber 71 | ad: GIDNumber 72 | shell: 73 | description: "Shell of the user" 74 | display_name: "Shell" 75 | self: True 76 | type: stringlist 77 | values: 78 | - /bin/bash 79 | - /bin/zsh 80 | - /bin/sh 81 | backends: 82 | ldap: shell 83 | ad: SHELL 84 | home: 85 | description: "Home user path" 86 | display_name: "Home" 87 | type: string 88 | autofill: 89 | function: home 90 | args: 91 | - $first-name 92 | - $last-name 93 | - /home/ 94 | backends: 95 | ldap: home 96 | ad: Home 97 | 98 | password: 99 | description: "Password of the user" 100 | display_name: "Password" 101 | self: True 102 | type: password 103 | backends: 104 | ldap: userPassword 105 | ad: unicodePwd 106 | logscript: 107 | description: "Windows login script" 108 | display_name: "Login script" 109 | type: fix 110 | value: login1.bat 111 | backends: 112 | ad: logonScript 113 | -------------------------------------------------------------------------------- /tests/cfg/attributes_test.yml: -------------------------------------------------------------------------------- 1 | cn: 2 | description: "First Name and Display Name" 3 | display_name: "Display Name" 4 | type: string 5 | weight: 30 6 | autofill: 7 | function: cn 8 | args: 9 | - $first-name 10 | - $name 11 | backends: 12 | ldap: cn 13 | 14 | first-name: 15 | description: "First name of the user" 16 | display_name: "First Name" 17 | search_displayed: True 18 | type: string 19 | weight: 20 20 | backends: 21 | ldap: givenName 22 | name: 23 | description: "Family name of the user" 24 | display_name: "Name" 25 | search_displayed: True 26 | weight: 10 27 | type: string 28 | backends: 29 | ldap: sn 30 | email: 31 | description: "Email of the user" 32 | display_name: "Email" 33 | search_displayed: True 34 | type: email 35 | weight: 40 36 | autofill: 37 | function: email 38 | args: 39 | - $first-name 40 | - $last-name 41 | - '@example.com' 42 | backends: 43 | ldap: mail 44 | uid: 45 | description: "UID of the user" 46 | display_name: "UID" 47 | search_displayed: True 48 | key: True 49 | type: string 50 | weight: 50 51 | autofill: 52 | function: uid 53 | args: 54 | - $first-name 55 | - $last-name 56 | backends: 57 | ldap: uid 58 | uidNumber: 59 | description: "User ID Number of the user" 60 | display_name: "UID Number" 61 | weight: 60 62 | type: int 63 | autofill: 64 | function: uidNumber 65 | args: 66 | - $first-name 67 | - $last-name 68 | backends: 69 | ldap: uidNumber 70 | gidNumber: 71 | description: "Group ID Number of the user" 72 | display_name: "GID Number" 73 | weight: 70 74 | type: int 75 | default: 10000 76 | backends: 77 | ldap: gidNumber 78 | shell: 79 | description: "Shell of the user" 80 | display_name: "Shell" 81 | weight: 80 82 | self: True 83 | type: stringlist 84 | values: 85 | - /bin/bash 86 | - /bin/zsh 87 | - /bin/sh 88 | backends: 89 | ldap: loginShell 90 | home: 91 | description: "Home user path" 92 | display_name: "Home" 93 | weight: 90 94 | type: string 95 | autofill: 96 | function: home 97 | args: 98 | - $first-name 99 | - $last-name 100 | - /home/ 101 | backends: 102 | ldap: homeDirectory 103 | 104 | password: 105 | description: "Password of the user" 106 | display_name: "Password" 107 | weight: 31 108 | self: True 109 | type: password 110 | backends: 111 | ldap: userPassword 112 | -------------------------------------------------------------------------------- /tests/cfg/attributes_wrong_type.yml: -------------------------------------------------------------------------------- 1 | cn: 2 | description: "Firt Name and Display Name" 3 | display_name: "Display Name" 4 | type: notatype 5 | key: True 6 | weight: 10 7 | autofill: 8 | function: cn 9 | args: 10 | - $first-name 11 | - $name 12 | backends: 13 | ldap: cn 14 | ad: cn 15 | first-name: 16 | description: "First name of the user" 17 | display_name: "First Name" 18 | type: string 19 | weight: 10 20 | backends: 21 | ldap: givenName 22 | ad: givenName 23 | password: 24 | description: "Password of the user" 25 | display_name: "Password" 26 | weight: 31 27 | self: True 28 | type: password 29 | backends: 30 | ad: unicodePwd 31 | -------------------------------------------------------------------------------- /tests/cfg/ldapcherry.ini: -------------------------------------------------------------------------------- 1 | # global parameters 2 | [global] 3 | 4 | # listing interface 5 | server.socket_host = '127.0.0.1' 6 | # port 7 | server.socket_port = 8080 8 | # number of threads 9 | server.thread_pool = 8 10 | #don't show traceback on error 11 | request.show_tracebacks = False 12 | 13 | # log configuration 14 | # /!\ you can't have multiple log handlers 15 | ##################################### 16 | # configuration to log in files # 17 | ##################################### 18 | ## logger 'file' for access log 19 | #log.access_handler = 'file' 20 | ## logger syslog for error and ldapcherry log 21 | #log.error_handler = 'file' 22 | ## access log file 23 | #log.access_file = '/tmp/ldapcherry_access.log' 24 | ## error and ldapcherry log file 25 | #log.error_file = '/tmp/ldapcherry_error.log' 26 | 27 | ##################################### 28 | # configuration to log in syslog # 29 | ##################################### 30 | # logger syslog for access log 31 | #log.access_handler = 'syslog' 32 | ## logger syslog for error and ldapcherry log 33 | log.error_handler = 'syslog' 34 | 35 | ##################################### 36 | # configuration to not log at all # 37 | ##################################### 38 | # logger none for access log 39 | log.access_handler = 'syslog' 40 | # logger none for error and ldapcherry log 41 | #log.error_handler = 'none' 42 | 43 | # log level 44 | log.level = 'debug' 45 | 46 | # session configuration 47 | # activate session 48 | tools.sessions.on = True 49 | # session timeout 50 | tools.sessions.timeout = 10 51 | # file session storage(to use if multiple processes, 52 | # default is in RAM and per process) 53 | #tools.sessions.storage_type = "file" 54 | # session 55 | #tools.sessions.storage_path = "/var/lib/ldapcherry/sessions" 56 | 57 | [attributes] 58 | 59 | # file discribing form content 60 | attributes.file = './tests/cfg/attributes.yml' 61 | 62 | [roles] 63 | 64 | # file listing roles 65 | roles.file = './tests/cfg/roles.yml' 66 | 67 | [backends] 68 | 69 | ldap.module = 'ldapcherry.backend.backendLdap' 70 | ldap.groupdn = 'ou=groups,dc=example,dc=org' 71 | ldap.userdn = 'ou=people,dc=example,dc=org' 72 | ldap.binddn = 'cn=dnscherry,dc=example,dc=org' 73 | ldap.password = 'password' 74 | ldap.uri = 'ldap://ldap.ldapcherry.org:390' 75 | ldap.ca = '/etc/dnscherry/TEST-cacert.pem' 76 | ldap.starttls = 'off' 77 | ldap.checkcert = 'off' 78 | ldap.user_filter_tmpl = '(uid=%(username)s)' 79 | ldap.group_filter_tmpl = '(member=%(userdn)s)' 80 | ldap.search_filter_tmpl = '(|(uid=%(searchstring)s*)(sn=%(searchstring)s*))' 81 | ldap.objectclasses = 'top, person, organizationalPerson, user' 82 | ldap.dn_user_attr = 'uid' 83 | ldap.timeout = 1 84 | 85 | ad.module = 'ldapcherry.backend.backendAD' 86 | ad.domain = 'dc.ldapcherry.org' 87 | ad.login = 'administrator' 88 | ad.password = 'qwertyP455' 89 | ad.uri = 'ldap://ldap.ldapcherry.org' 90 | 91 | # authentification parameters 92 | [auth] 93 | 94 | # Auth mode 95 | # * and: user must authenticate on all backends 96 | # * or: user must authenticate on one of the backend 97 | # * none: disable authentification 98 | # * custom: custom authentification module (need auth.module param) 99 | auth.mode = 'or' 100 | 101 | # custom auth module to load 102 | #auth.module = 'ldapcherry.auth.modNone' 103 | 104 | [ppolicy] 105 | 106 | # password policy module 107 | ppolicy.module = 'ldapcherry.ppolicy.simple' 108 | 109 | # parameters of the module 110 | min_length = 8 111 | min_upper = 1 112 | min_digit = 1 113 | 114 | # resources parameters 115 | [resources] 116 | # templates directory 117 | templates.dir = './resources/templates/' 118 | 119 | [/static] 120 | tools.staticdir.on = True 121 | tools.staticdir.dir = './resources/static/' 122 | -------------------------------------------------------------------------------- /tests/cfg/ldapcherry_test.ini: -------------------------------------------------------------------------------- 1 | # global parameters 2 | [global] 3 | 4 | # listing interface 5 | server.socket_host = '127.0.0.1' 6 | # port 7 | server.socket_port = 8080 8 | # number of threads 9 | server.thread_pool = 8 10 | #don't show traceback on error 11 | request.show_tracebacks = False 12 | 13 | # log configuration 14 | # /!\ you can't have multiple log handlers 15 | ##################################### 16 | # configuration to log in files # 17 | ##################################### 18 | ## logger 'file' for access log 19 | #log.access_handler = 'file' 20 | ## logger syslog for error and ldapcherry log 21 | #log.error_handler = 'file' 22 | ## access log file 23 | #log.access_file = '/tmp/ldapcherry_access.log' 24 | ## error and ldapcherry log file 25 | #log.error_file = '/tmp/ldapcherry_error.log' 26 | 27 | ##################################### 28 | # configuration to log in syslog # 29 | ##################################### 30 | # logger syslog for access log 31 | #log.access_handler = 'syslog' 32 | ## logger syslog for error and ldapcherry log 33 | log.error_handler = 'syslog' 34 | 35 | ##################################### 36 | # configuration to not log at all # 37 | ##################################### 38 | # logger none for access log 39 | log.access_handler = 'none' 40 | # logger none for error and ldapcherry log 41 | #log.error_handler = 'none' 42 | 43 | # log level 44 | log.level = 'debug' 45 | 46 | # session configuration 47 | # activate session 48 | tools.sessions.on = True 49 | # session timeout 50 | tools.sessions.timeout = 10 51 | # file session storage(to use if multiple processes, 52 | # default is in RAM and per process) 53 | #tools.sessions.storage_type = "file" 54 | # session 55 | #tools.sessions.storage_path = "/var/lib/ldapcherry/sessions" 56 | 57 | [attributes] 58 | 59 | # file discribing form content 60 | attributes.file = './tests/cfg/attributes_test.yml' 61 | 62 | [roles] 63 | 64 | # file listing roles 65 | roles.file = './tests/cfg/roles_test.yml' 66 | 67 | [backends] 68 | 69 | # name of the module 70 | ldap.module = 'ldapcherry.backend.backendLdap' 71 | 72 | # uri of the ldap directory 73 | ldap.uri = 'ldap://ldap.ldapcherry.org:390' 74 | # ca to use for ssl/tls connexion 75 | #ldap.ca = '/etc/dnscherry/TEST-cacert.pem' 76 | # use start tls 77 | ldap.starttls = 'off' 78 | # check server certificate (for tls) 79 | ldap.checkcert = 'off' 80 | # bind dn to the ldap 81 | ldap.binddn = 'cn=dnscherry,dc=example,dc=org' 82 | # password of the bind dn 83 | ldap.password = 'password' 84 | # timeout of ldap connexion (in second) 85 | ldap.timeout = 1 86 | 87 | # groups dn 88 | ldap.groupdn = 'ou=group,dc=example,dc=org' 89 | # users dn 90 | ldap.userdn = 'ou=people,dc=example,dc=org' 91 | # ldapsearch filter to get a user 92 | ldap.user_filter_tmpl = '(uid=%(username)s)' 93 | # ldapsearch filter to get groups of a user 94 | ldap.group_filter_tmpl = '(member=uid=%(username)s,ou=People,dc=example,dc=org)' 95 | # filter to search users 96 | ldap.search_filter_tmpl = '(|(uid=%(searchstring)s*)(sn=%(searchstring)s*))' 97 | 98 | # ldap group attributes and how to fill them 99 | ldap.group_attr.member = "%(dn)s" 100 | #ldap.group_attr.memberUid = "%(uid)s" 101 | # object classes of a user entry 102 | ldap.objectclasses = 'top, person, posixAccount, inetOrgPerson' 103 | # dn entry attribute for an ldap user 104 | ldap.dn_user_attr = 'uid' 105 | 106 | 107 | # authentification parameters 108 | [auth] 109 | 110 | # Auth mode 111 | # * and: user must authenticate on all backends 112 | # * or: user must authenticate on one of the backend 113 | # * none: disable authentification 114 | # * custom: custom authentification module (need auth.module param) 115 | auth.mode = 'none' 116 | 117 | # custom auth module to load 118 | #auth.module = 'ldapcherry.auth.modNone' 119 | 120 | # resources parameters 121 | [resources] 122 | # templates directory 123 | templates.dir = './resources/templates/' 124 | 125 | [/static] 126 | tools.staticdir.on = True 127 | tools.staticdir.dir = './resources/static/' 128 | -------------------------------------------------------------------------------- /tests/cfg/nested.yml: -------------------------------------------------------------------------------- 1 | users: 2 | backends_groups: 3 | ad: [Domain Users] 4 | ldap: ['cn=users,ou=group,dc=example,dc=com'] 5 | display_name: Simple Users 6 | description: description 7 | subroles: 8 | admin-lv2: 9 | LC_admins: true 10 | backends_groups: 11 | ad: [Domain Users] 12 | ldap: ['cn=nagios admins,ou=group,dc=example,dc=com'] 13 | display_name: Administrators Level 2 14 | description: description 15 | subroles: 16 | admin-lv3: 17 | backends_groups: 18 | ad: [Domain Users, Administrators, Domain Controllers] 19 | ldap: ['cn=dns admins,ou=group,dc=example,dc=com', 'cn=nagios admins,ou=group,dc=example,dc=com', 20 | 'cn=puppet admins,ou=group,dc=example,dc=com',] 21 | display_name: Administrators Level 3 22 | description: description 23 | subroles: {} 24 | developers: 25 | backends_groups: 26 | ad: [Domain Users] 27 | ldap: ['cn=developers,ou=group,dc=example,dc=com'] 28 | display_name: Developpers 29 | description: description 30 | subroles: {} 31 | -------------------------------------------------------------------------------- /tests/cfg/roles.yml: -------------------------------------------------------------------------------- 1 | admin-lv3: 2 | display_name: Administrators Level 3 3 | description: description 4 | backends_groups: 5 | ldap: 6 | - cn=dns admins,ou=group,dc=example,dc=com 7 | - cn=nagios admins,ou=group,dc=example,dc=com 8 | - cn=puppet admins,ou=group,dc=example,dc=com 9 | - cn=users,ou=group,dc=example,dc=com 10 | ad: 11 | - Domain Users 12 | - Administrators 13 | - Domain Controllers 14 | 15 | admin-lv2: 16 | display_name: Administrators Level 2 17 | description: description 18 | LC_admins: True 19 | backends_groups: 20 | ldap: 21 | - cn=nagios admins,ou=group,dc=example,dc=com 22 | - cn=users,ou=group,dc=example,dc=com 23 | ad: 24 | - Domain Users 25 | 26 | developers: 27 | display_name: Developpers 28 | description: description 29 | backends_groups: 30 | ldap: 31 | - cn=developers,ou=group,dc=example,dc=com 32 | - cn=users,ou=group,dc=example,dc=com 33 | ad: 34 | - Domain Users 35 | 36 | users: 37 | display_name: Simple Users 38 | description: description 39 | backends_groups: 40 | ldap: 41 | - cn=users,ou=group,dc=example,dc=com 42 | ad: 43 | - Domain Users 44 | -------------------------------------------------------------------------------- /tests/cfg/roles_adldap.yml: -------------------------------------------------------------------------------- 1 | admin-lv3: 2 | display_name: Administrators Level 3 3 | description: Super administrators of the system 4 | backends_groups: 5 | # ldap: 6 | # - cn=dns admins,ou=Group,dc=example,dc=org 7 | # - cn=nagios admins,ou=Group,dc=example,dc=org 8 | # - cn=puppet admins,ou=Group,dc=example,dc=org 9 | # - cn=users,ou=Group,dc=example,dc=org 10 | ad: 11 | - Administrators 12 | - Domain Controllers 13 | - Group Policy Creator Owners 14 | 15 | admin-lv2: 16 | display_name: Administrators Level 2 17 | description: Basic administrators of the system 18 | LC_admins: True 19 | backends_groups: 20 | # ldap: 21 | # - cn=nagios admins,ou=Group,dc=example,dc=org 22 | # - cn=users,ou=Group,dc=example,dc=org 23 | ad: 24 | - Administrators 25 | 26 | #developers: 27 | # display_name: Developpers 28 | # description: Developpers of the system 29 | # backends_groups: 30 | # ldap: 31 | # - cn=developers,ou=Group,dc=example,dc=org 32 | # - cn=users,ou=Group,dc=example,dc=org 33 | 34 | #users: 35 | # display_name: Simple Users 36 | # description: Basic users of the system 37 | # backends_groups: 38 | ## ldap: 39 | ## - cn=users,ou=Group,dc=example,dc=org 40 | # ad: 41 | # - Domain Users 42 | -------------------------------------------------------------------------------- /tests/cfg/roles_content_dup.yml: -------------------------------------------------------------------------------- 1 | admin -lv3: 2 | display_name: Administrators Level 3 3 | LC_admins: True 4 | description: description 5 | backends_groups: 6 | ldap: 7 | - cn=dns admins,ou=group,dc=example,dc=com 8 | - cn=nagios admins,ou=group,dc=example,dc=com 9 | - cn=puppet admins,ou=group,dc=example,dc=com 10 | - cn=users,ou=group,dc=example,dc=com 11 | ad: 12 | - Domain Users 13 | - Administrators 14 | - Domain Controllers 15 | 16 | admin -lv2: 17 | display_name: Administrators Level 2 18 | description: description 19 | backends_groups: 20 | ldap: 21 | - cn=nagios admins,ou=group,dc=example,dc=com 22 | - cn=users,ou=group,dc=example,dc=com 23 | ad: 24 | - Domain Users 25 | 26 | developers: 27 | display_name: Developpers 28 | description: description 29 | backends_groups: 30 | ldap: 31 | - cn=developers,ou=group,dc=example,dc=com 32 | - cn=users,ou=group,dc=example,dc=com 33 | ad: 34 | - Domain Users 35 | 36 | users: 37 | display_name: Simple Users 38 | description: description 39 | backends_groups: 40 | ldap: 41 | - cn=users,ou=group,dc=example,dc=com 42 | ad: 43 | - Domain Users 44 | 45 | users2: 46 | display_name: Simple Users 2 47 | description: description 48 | backends_groups: 49 | ldap: 50 | - cn=users,ou=group,dc=example,dc=com 51 | ad: 52 | - Domain Users 53 | -------------------------------------------------------------------------------- /tests/cfg/roles_key_dup.yml: -------------------------------------------------------------------------------- 1 | admin -lv3: 2 | display_name: Administrators Level 3 3 | description: description 4 | LC_admins: True 5 | backends_groups: 6 | ldap: 7 | - cn=dns admins,ou=group,dc=example,dc=com 8 | - cn=nagios admins,ou=group,dc=example,dc=com 9 | - cn=puppet admins,ou=group,dc=example,dc=com 10 | - cn=users,ou=group,dc=example,dc=com 11 | ad: 12 | - Domain Users 13 | - Administrators 14 | - Domain Controllers 15 | 16 | admin -lv3: 17 | display_name: Administrators Level 2 18 | description: description 19 | backends_groups: 20 | ldap: 21 | - cn=nagios admins,ou=group,dc=example,dc=com 22 | - cn=users,ou=group,dc=example,dc=com 23 | ad: 24 | - Domain Users 25 | 26 | developers: 27 | display_name: Developpers 28 | description: description 29 | backends_groups: 30 | ldap: 31 | - cn=developers,ou=group,dc=example,dc=com 32 | - cn=users,ou=group,dc=example,dc=com 33 | ad: 34 | - Domain Users 35 | 36 | users: 37 | display_name: Simple Users 38 | description: description 39 | backends_groups: 40 | ldap: 41 | - cn=users,ou=group,dc=example,dc=com 42 | ad: 43 | - Domain Users 44 | -------------------------------------------------------------------------------- /tests/cfg/roles_missing_backends.yml: -------------------------------------------------------------------------------- 1 | admin-lv3: 2 | display_name: Administrators Level 3 3 | description: description 4 | LC_admins: True 5 | backends_groups: 6 | ldap: 7 | - cn=dns admins,ou=group,dc=example,dc=com 8 | - cn=nagios admins,ou=group,dc=example,dc=com 9 | - cn=puppet admins,ou=group,dc=example,dc=com 10 | - cn=users,ou=group,dc=example,dc=com 11 | ad: 12 | - Domain Users 13 | - Administrators 14 | - Domain Controllers 15 | 16 | admin-lv2: 17 | display_name: Administrators Level 2 18 | description: description 19 | 20 | developers: 21 | display_name: Developpers 22 | description: description 23 | backends_groups: 24 | ldap: 25 | - cn=developers,ou=group,dc=example,dc=com 26 | - cn=users,ou=group,dc=example,dc=com 27 | ad: 28 | - Domain Users 29 | 30 | users: 31 | display_name: Simple Users 32 | description: description 33 | backends_groups: 34 | ldap: 35 | - cn=users,ou=group,dc=example,dc=com 36 | ad: 37 | - Domain Users 38 | -------------------------------------------------------------------------------- /tests/cfg/roles_missing_diplay_name.yml: -------------------------------------------------------------------------------- 1 | admin-lv3: 2 | display_name: Administrators Level 3 3 | LC_admins: True 4 | description: description 5 | backends_groups: 6 | ldap: 7 | - cn=dns admins,ou=group,dc=example,dc=com 8 | - cn=nagios admins,ou=group,dc=example,dc=com 9 | - cn=puppet admins,ou=group,dc=example,dc=com 10 | - cn=users,ou=group,dc=example,dc=com 11 | ad: 12 | - Domain Users 13 | - Administrators 14 | - Domain Controllers 15 | 16 | admin-lv2: 17 | description: description 18 | backends_groups: 19 | ldap: 20 | - cn=nagios admins,ou=group,dc=example,dc=com 21 | - cn=users,ou=group,dc=example,dc=com 22 | ad: 23 | - Domain Users 24 | 25 | developers: 26 | display_name: Developpers 27 | description: description 28 | backends_groups: 29 | ldap: 30 | - cn=developers,ou=group,dc=example,dc=com 31 | - cn=users,ou=group,dc=example,dc=com 32 | ad: 33 | - Domain Users 34 | 35 | users: 36 | display_name: Simple Users 37 | description: description 38 | backends_groups: 39 | ldap: 40 | - cn=users,ou=group,dc=example,dc=com 41 | ad: 42 | - Domain Users 43 | -------------------------------------------------------------------------------- /tests/cfg/roles_test.yml: -------------------------------------------------------------------------------- 1 | admin-lv3: 2 | display_name: Administrators Level 3 3 | description: Super administrators of the system 4 | backends_groups: 5 | ldap: 6 | - cn=dns admins,ou=Group,dc=example,dc=org 7 | - cn=nagios admins,ou=Group,dc=example,dc=org 8 | - cn=puppet admins,ou=Group,dc=example,dc=org 9 | - cn=users,ou=Group,dc=example,dc=org 10 | 11 | admin-lv2: 12 | display_name: Administrators Level 2 13 | description: Basic administrators of the system 14 | LC_admins: True 15 | backends_groups: 16 | ldap: 17 | - cn=nagios admins,ou=Group,dc=example,dc=org 18 | - cn=users,ou=Group,dc=example,dc=org 19 | 20 | developers: 21 | display_name: Developpers 22 | description: Developpers of the system 23 | backends_groups: 24 | ldap: 25 | - cn=developers,ou=Group,dc=example,dc=org 26 | - cn=users,ou=Group,dc=example,dc=org 27 | 28 | users: 29 | display_name: Simple Users 30 | description: Basic users of the system 31 | backends_groups: 32 | ldap: 33 | - cn=users,ou=Group,dc=example,dc=org 34 | -------------------------------------------------------------------------------- /tests/cfg/wrong_ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEpDCCA4ygAwIBAgIJAJPjqWBPSpcrMA0GCSqGSIb3DQEBBQUAMIGSMQswCQYD 3 | VQQGEwJGUjELMAkGA1UECBMCRlIxDjAMBgNVBAcTBVBhcmlzMQ4wDAYDVQQKEwVQ 4 | YXJpczERMA8GA1UECxMIY2hhbmdlbWUxETAPBgNVBAMTCGNoYW5nZW1lMREwDwYD 5 | VQQpEwhjaGFuZ2VtZTEdMBsGCSqGSIb3DQEJARYOa2Frd2FAa2Frd2EuZnIwHhcN 6 | MTIwNzIxMTgwMzExWhcNMjIwNzE5MTgwMzExWjCBkjELMAkGA1UEBhMCRlIxCzAJ 7 | BgNVBAgTAkZSMQ4wDAYDVQQHEwVQYXJpczEOMAwGA1UEChMFUGFyaXMxETAPBgNV 8 | BAsTCGNoYW5nZW1lMREwDwYDVQQDEwhjaGFuZ2VtZTERMA8GA1UEKRMIY2hhbmdl 9 | bWUxHTAbBgkqhkiG9w0BCQEWDmtha3dhQGtha3dhLmZyMIIBIjANBgkqhkiG9w0B 10 | AQEFAAOCAQ8AMIIBCgKCAQEA2JAYpMeudhVLgUOCdnA4a4R+sGv7dNxcrBTK9Eh3 11 | PHbCwBtAfX8J2NXjKiSNlZLw2xc5A7wEks7JxieynBfClL3kruZ2pj9yxT4BH4ro 12 | fY560b887miofiqKjB1dEnpoOfQNxUwUKVdKlOU0U8oteHwEnet8EbJ3Th4bkftz 13 | Bk8PYDOCt2x+SK6mHJz8yOsezsLfsrNdOLlY+dDrgZFmIGekTdo7okGaiPIndr1s 14 | OYcDLlow188oHnUZ8I9uPQW6Tk6gveh65sDc4ThpdrF8dV7UQxOrP+lBTcbrQNf0 15 | dMy2UDuA4TauIA6o6JsxtBbsBRph4vmgGXc1AGfmC2QXqwIDAQABo4H6MIH3MB0G 16 | A1UdDgQWBBTS1NffwUVvC47DSsSh5WCPGXMvxDCBxwYDVR0jBIG/MIG8gBTS1Nff 17 | wUVvC47DSsSh5WCPGXMvxKGBmKSBlTCBkjELMAkGA1UEBhMCRlIxCzAJBgNVBAgT 18 | AkZSMQ4wDAYDVQQHEwVQYXJpczEOMAwGA1UEChMFUGFyaXMxETAPBgNVBAsTCGNo 19 | YW5nZW1lMREwDwYDVQQDEwhjaGFuZ2VtZTERMA8GA1UEKRMIY2hhbmdlbWUxHTAb 20 | BgkqhkiG9w0BCQEWDmtha3dhQGtha3dhLmZyggkAk+OpYE9KlyswDAYDVR0TBAUw 21 | AwEB/zANBgkqhkiG9w0BAQUFAAOCAQEATGrU92RcniJ5QkOPLR/Zy2850jtknHKq 22 | VynTH5+smoRqDm6MJNSXb4Hy437qRFZdIyPcIXLgn+C31z0yfkSxW6MoGvYsWo86 23 | SKjow/OG4XQcHiOr0ydOSqdWL9EXWq+0DwnwWcmaFpuRhN2pK4fZmIcokRBiIbv0 24 | xnuyFvCTpsEOJHaYRQdE71omb47OBFhSA+ytGihmD6FycNqP9mriA0fPw2o/oVSd 25 | WC55yNfi9JqimfH/AN2ApMXD6TQD9JyyNJ2Qciwf7WsU+h3I/qIS15RsG+VUFm5E 26 | D62QGIMu6rRj06GO4e7+0+doiHvV9b8rk37aMOEhWmTw2v6aHJcGHw== 27 | -----END CERTIFICATE----- 28 | -------------------------------------------------------------------------------- /tests/disable.py: -------------------------------------------------------------------------------- 1 | import os 2 | def travis_disabled(f): 3 | def _decorator(f): 4 | print('test has been disabled on travis') 5 | if 'TRAVIS' in os.environ and os.environ['TRAVIS'] == 'yes': 6 | return _decorator 7 | else: 8 | return f 9 | 10 | def slow_disabled(f): 11 | def _decorator(f): 12 | print('test has been disabled by env var LCNOSLOW') 13 | if 'LCNOSLOW' in os.environ and os.environ['LCNOSLOW'] == 'yes': 14 | return _decorator 15 | else: 16 | return f 17 | -------------------------------------------------------------------------------- /tests/test_env/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | apt update 4 | 5 | DEBIAN_FRONTEND=noninteractive apt-get install ldap-utils slapd -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -f -q -y 6 | 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 7 | DEBIAN_FRONTEND=noninteractive apt-get install winbind -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -f -q -y 8 | 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 9 | 10 | [ -e '/etc/default/slapd' ] && rm -rf /etc/default/slapd 11 | cp -r `dirname $0`/etc/default/slapd /etc/default/slapd 12 | [ -e '/etc/ldap' ] && rm -rf /etc/ldap 13 | cp -r `dirname $0`/etc/ldap /etc/ldap 14 | [ -e '/etc/ldapcherry' ] && rm -rf /etc/ldapcherry 15 | cp -r `dirname $0`/etc/ldapcherry /etc/ldapcherry 16 | 17 | cd `dirname $0`/../../ 18 | sudo sed -i "s%template_dir.*%template_dir = '`pwd`/resources/templates/'%" /etc/ldapcherry/ldapcherry.ini 19 | sudo sed -i "s%tools.staticdir.dir.*%tools.staticdir.dir = '`pwd`/resources/static/'%" /etc/ldapcherry/ldapcherry.ini 20 | 21 | chown -R openldap:openldap /etc/ldap/ 22 | /etc/init.d/slapd restart 23 | ldapadd -c -H ldap://localhost:390 -x -D "cn=admin,dc=example,dc=org" -f /etc/ldap/content.ldif -w password 24 | if grep -q '127.0.0.1' /etc/hosts && ! grep -q 'ldap.ldapcherry.org' /etc/hosts 25 | then 26 | sed -i "s/\(127.0.0.1.*\)/\1 ldap.ldapcherry.org ad.ldapcherry.org ldap.dnscherry.org/" /etc/hosts 27 | else 28 | echo '127.0.0.1 ldap.ldapcherry.org ad.ldapcherry.org ldap.dnscherry.org' >> /etc/hosts 29 | fi 30 | cat /etc/hosts 31 | 32 | 33 | df -h 34 | 35 | find /var/log/samba/ -type f -exec rm -f {} \; 36 | 37 | smbconffile=/etc/samba/smb.conf 38 | domain=dc 39 | realm=dc.ldapcherry.org 40 | sambadns=SAMBA_INTERNAL 41 | targetdir=/var/lib/samba/ 42 | role=dc 43 | sambacmd=samba-tool 44 | adpass=qwertyP455 45 | 46 | systemctl unmask samba-ad-dc 47 | 48 | hostname ad.ldapcherry.org 49 | pkill -9 dnsmasq 50 | pkill -9 samba 51 | 52 | kill -9 `cat /var/run/samba/smbd.pid` 53 | rm -f /var/run/samba/smbd.pid 54 | kill -9 `cat /var/run/samba/nmbd.pid` 55 | rm -f /var/run/samba/nmbd.pid 56 | rm -rf /var/run/samba 57 | 58 | echo "deploy AD" 59 | printf '' > "${smbconffile}" && \ 60 | ${sambacmd} domain provision ${hostip} \ 61 | --domain="${domain}" --realm="${realm}" --dns-backend="${sambadns}" \ 62 | --targetdir="${targetdir}" --use-rfc2307 \ 63 | --configfile="${smbconffile}" --server-role="${role}" -d 1 --adminpass="${adpass}" 64 | 65 | 66 | echo "Move configuration" 67 | mv "${targetdir}/etc/smb.conf" "${smbconffile}" 68 | 69 | cat ${smbconffile} 70 | 71 | mv /var/lib/samba/private/krb5.conf /etc/krb5.conf 72 | 73 | sleep 15 74 | 75 | systemctl restart samba-ad-dc 76 | /etc/init.d/samba-ad-dc restart 77 | 78 | cat /var/log/samba/* 79 | 80 | sleep 5 81 | 82 | samba-tool domain passwordsettings set -d 1 --complexity off 83 | samba-tool domain passwordsettings set -d 1 --min-pwd-length 0 84 | systemctl status samba-ad-dc 85 | ss -apn | grep samba 86 | -------------------------------------------------------------------------------- /tests/test_env/etc/default/slapd: -------------------------------------------------------------------------------- 1 | # Default location of the slapd.conf file or slapd.d cn=config directory. If 2 | # empty, use the compiled-in default (/etc/ldap/slapd.d with a fallback to 3 | # /etc/ldap/slapd.conf). 4 | SLAPD_CONF= 5 | 6 | # System account to run the slapd server under. If empty the server 7 | # will run as root. 8 | SLAPD_USER="openldap" 9 | 10 | # System group to run the slapd server under. If empty the server will 11 | # run in the primary group of its user. 12 | SLAPD_GROUP="openldap" 13 | 14 | # Path to the pid file of the slapd server. If not set the init.d script 15 | # will try to figure it out from $SLAPD_CONF (/etc/ldap/slapd.conf by 16 | # default) 17 | SLAPD_PIDFILE= 18 | 19 | # slapd normally serves ldap only on all TCP-ports 389. slapd can also 20 | # service requests on TCP-port 636 (ldaps) and requests via unix 21 | # sockets. 22 | # Example usage: 23 | SLAPD_SERVICES="ldap://127.0.0.1:390/ ldaps://127.0.0.1:637/ ldapi:///" 24 | #SLAPD_SERVICES="ldap:/// ldapi:/// ldaps:///" 25 | 26 | # If SLAPD_NO_START is set, the init script will not start or restart 27 | # slapd (but stop will still work). Uncomment this if you are 28 | # starting slapd via some other means or if you don't want slapd normally 29 | # started at boot. 30 | #SLAPD_NO_START=1 31 | 32 | # If SLAPD_SENTINEL_FILE is set to path to a file and that file exists, 33 | # the init script will not start or restart slapd (but stop will still 34 | # work). Use this for temporarily disabling startup of slapd (when doing 35 | # maintenance, for example, or through a configuration management system) 36 | # when you don't want to edit a configuration file. 37 | SLAPD_SENTINEL_FILE=/etc/ldap/noslapd 38 | 39 | # For Kerberos authentication (via SASL), slapd by default uses the system 40 | # keytab file (/etc/krb5.keytab). To use a different keytab file, 41 | # uncomment this line and change the path. 42 | #export KRB5_KTNAME=/etc/krb5.keytab 43 | 44 | # Additional options to pass to slapd 45 | SLAPD_OPTIONS="" 46 | -------------------------------------------------------------------------------- /tests/test_env/etc/ldap/content.ldif: -------------------------------------------------------------------------------- 1 | dn: dc=example,dc=org 2 | dc: example 3 | objectClass: top 4 | objectClass: domain 5 | 6 | dn: cn=dnscherry,dc=example,dc=org 7 | objectClass: simpleSecurityObject 8 | objectClass: organizationalRole 9 | cn: dnscherry 10 | description: [applicative account] 11 | userPassword: password 12 | 13 | dn: cn=ldapcherry,dc=example,dc=org 14 | objectClass: simpleSecurityObject 15 | objectClass: organizationalRole 16 | cn: ldapcherry 17 | description: [applicative account] 18 | userPassword: password 19 | 20 | dn: ou=People,dc=example,dc=org 21 | ou: People 22 | objectClass: top 23 | objectclass: organizationalunit 24 | 25 | dn: ou=Groups,dc=example,dc=org 26 | ou: Groups 27 | objectClass: top 28 | objectclass: organizationalunit 29 | 30 | dn: ou=Group,dc=example,dc=org 31 | ou: Group 32 | objectClass: top 33 | objectclass: organizationalunit 34 | 35 | 36 | dn: cn=Sheri Smith,ou=people,dc=example,dc=org 37 | objectclass: inetOrgPerson 38 | cn: Sheri Smith 39 | sn: smith 40 | uid: ssmith 41 | userpassword: passwordsmith 42 | carlicense: HERCAR 125 43 | homephone: 555-111-2225 44 | mail: s.smith@example.com 45 | mail: ssmith@example.com 46 | mail: sheri.smith@example.com 47 | 48 | dn: cn=John Smith,ou=people,dc=example,dc=org 49 | objectclass: inetOrgPerson 50 | cn: John Smith 51 | sn: Smith 52 | uid: jsmith 53 | userpassword: passwordsmith 54 | carlicense: HISCAR 125 55 | homephone: 555-111-2225 56 | mail: j.smith@example.com 57 | mail: jsmith@example.com 58 | mail: jsmith.smith@example.com 59 | 60 | dn: cn=John Watson,ou=people,dc=example,dc=org 61 | objectclass: inetOrgPerson 62 | cn: John Watson 63 | sn: watson 64 | uid: jwatsoné 65 | userpassword: passwordwatsoné 66 | carlicense: HERCAR 125 67 | homephone: 555-111-2225 68 | mail: j.watson@example.com 69 | 70 | dn: cn=itpeople,ou=groups,dc=example,dc=org 71 | objectclass: groupofnames 72 | cn: itpeople 73 | description: IT security group 74 | member: cn=John Watson,ou=people,dc=example,dc=org 75 | 76 | dn: cn=hrpeople,ou=groups,dc=example,dc=org 77 | objectclass: groupofnames 78 | cn: itpeople 79 | description: IT security group 80 | member: cn=John Smith,ou=people,dc=example,dc=org 81 | 82 | dn: cn=nagios admins,ou=group,dc=example,dc=org 83 | objectclass: groupofnames 84 | cn: nagios admins 85 | description: Nagios Administrators 86 | member: cn=Sheri Smith,ou=people,dc=example,dc=org 87 | 88 | dn: cn=dns admins,ou=group,dc=example,dc=org 89 | objectclass: groupofnames 90 | cn: dns admins 91 | description: Dns Administrators 92 | member: cn=Sheri Smith,ou=people,dc=example,dc=org 93 | 94 | dn: cn=puppet admins,ou=group,dc=example,dc=org 95 | objectclass: groupofnames 96 | cn: puppet admins 97 | description: Dns Administrators 98 | member: cn=Sheri Smith,ou=people,dc=example,dc=org 99 | 100 | dn: cn=users,ou=group,dc=example,dc=org 101 | objectclass: groupofnames 102 | cn: users 103 | description: Basic Users 104 | member: cn=Sheri Smith,ou=people,dc=example,dc=org 105 | 106 | dn: cn=developers,ou=group,dc=example,dc=org 107 | objectclass: groupofnames 108 | cn: developers 109 | description: Developpers 110 | member: cn=Sheri Smith,ou=people,dc=example,dc=org 111 | 112 | dn: cn=posixdev,ou=group,dc=example,dc=org 113 | objectclass: posixGroup 114 | cn: posixdev 115 | description: Developpers 116 | gidNumber: 10002 117 | memberUid: ssmith 118 | 119 | dn: cn=posixadm,ou=group,dc=example,dc=org 120 | objectclass: posixGroup 121 | cn: posixadm 122 | description: Administration 123 | gidNumber: 10001 124 | memberUid: ssmith 125 | -------------------------------------------------------------------------------- /tests/test_env/etc/ldap/slapd.d/cn=config.ldif: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify. 2 | # CRC32 7a6099db 3 | dn: cn=config 4 | objectClass: olcGlobal 5 | cn: config 6 | olcArgsFile: /var/run/slapd/slapd.args 7 | olcLogLevel: none 8 | olcPidFile: /var/run/slapd/slapd.pid 9 | olcToolThreads: 1 10 | structuralObjectClass: olcGlobal 11 | entryUUID: 38579c70-750a-103e-8489-9578878139e2 12 | creatorsName: cn=config 13 | createTimestamp: 20240312221838Z 14 | entryCSN: 20240312221838.644900Z#000000#000#000000 15 | modifiersName: cn=config 16 | modifyTimestamp: 20240312221838Z 17 | olcTLSCACertificateFile: /etc/ldap/ssl/TEST-cacert.pem 18 | olcTLSCertificateFile: /etc/ldap/ssl/ldap@dnscherry.org-cert.pem 19 | olcTLSCertificateKeyFile: /etc/ldap/ssl/ldap@dnscherry.org-key.pem 20 | -------------------------------------------------------------------------------- /tests/test_env/etc/ldap/slapd.d/cn=config/cn=module{0}.ldif: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify. 2 | # CRC32 baf86138 3 | dn: cn=module{0} 4 | objectClass: olcModuleList 5 | cn: module{0} 6 | olcModulePath: /usr/lib/ldap 7 | olcModuleLoad: {0}back_mdb 8 | structuralObjectClass: olcModuleList 9 | entryUUID: 3857cd3a-750a-103e-8491-9578878139e2 10 | creatorsName: cn=admin,cn=config 11 | createTimestamp: 20240312221838Z 12 | entryCSN: 20240312221838.646168Z#000000#000#000000 13 | modifiersName: cn=admin,cn=config 14 | modifyTimestamp: 20240312221838Z 15 | -------------------------------------------------------------------------------- /tests/test_env/etc/ldap/slapd.d/cn=config/cn=schema.ldif: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify. 2 | # CRC32 fff51a11 3 | dn: cn=schema 4 | objectClass: olcSchemaConfig 5 | cn: schema 6 | structuralObjectClass: olcSchemaConfig 7 | entryUUID: 3857a42c-750a-103e-848c-9578878139e2 8 | creatorsName: cn=admin,cn=config 9 | createTimestamp: 20240312221838Z 10 | entryCSN: 20240312221838.645118Z#000000#000#000000 11 | modifiersName: cn=admin,cn=config 12 | modifyTimestamp: 20240312221838Z 13 | -------------------------------------------------------------------------------- /tests/test_env/etc/ldap/slapd.d/cn=config/cn=schema/cn={3}inetorgperson.ldif: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify. 2 | # CRC32 e06c8c33 3 | dn: cn={3}inetorgperson 4 | objectClass: olcSchemaConfig 5 | cn: {3}inetorgperson 6 | olcAttributeTypes: {0}( 2.16.840.1.113730.3.1.1 NAME 'carLicense' DESC 'RFC279 7 | 8: vehicle license or registration plate' EQUALITY caseIgnoreMatch SUBSTR cas 8 | eIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) 9 | olcAttributeTypes: {1}( 2.16.840.1.113730.3.1.2 NAME 'departmentNumber' DESC ' 10 | RFC2798: identifies a department within an organization' EQUALITY caseIgnoreM 11 | atch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) 12 | olcAttributeTypes: {2}( 2.16.840.1.113730.3.1.241 NAME 'displayName' DESC 'RFC 13 | 2798: preferred name to be used when displaying entries' EQUALITY caseIgnoreM 14 | atch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SI 15 | NGLE-VALUE ) 16 | olcAttributeTypes: {3}( 2.16.840.1.113730.3.1.3 NAME 'employeeNumber' DESC 'RF 17 | C2798: numerically identifies an employee within an organization' EQUALITY ca 18 | seIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.12 19 | 1.1.15 SINGLE-VALUE ) 20 | olcAttributeTypes: {4}( 2.16.840.1.113730.3.1.4 NAME 'employeeType' DESC 'RFC2 21 | 798: type of employment for a person' EQUALITY caseIgnoreMatch SUBSTR caseIgn 22 | oreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) 23 | olcAttributeTypes: {5}( 0.9.2342.19200300.100.1.60 NAME 'jpegPhoto' DESC 'RFC2 24 | 798: a JPEG image' SYNTAX 1.3.6.1.4.1.1466.115.121.1.28 ) 25 | olcAttributeTypes: {6}( 2.16.840.1.113730.3.1.39 NAME 'preferredLanguage' DESC 26 | 'RFC2798: preferred written or spoken language for a person' EQUALITY caseIg 27 | noreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1. 28 | 15 SINGLE-VALUE ) 29 | olcAttributeTypes: {7}( 2.16.840.1.113730.3.1.40 NAME 'userSMIMECertificate' D 30 | ESC 'RFC2798: PKCS#7 SignedData used to support S/MIME' SYNTAX 1.3.6.1.4.1.14 31 | 66.115.121.1.5 ) 32 | olcAttributeTypes: {8}( 2.16.840.1.113730.3.1.216 NAME 'userPKCS12' DESC 'RFC2 33 | 798: personal identity information, a PKCS #12 PFX' SYNTAX 1.3.6.1.4.1.1466.1 34 | 15.121.1.5 ) 35 | olcObjectClasses: {0}( 2.16.840.1.113730.3.2.2 NAME 'inetOrgPerson' DESC 'RFC2 36 | 798: Internet Organizational Person' SUP organizationalPerson STRUCTURAL MAY 37 | ( audio $ businessCategory $ carLicense $ departmentNumber $ displayName $ em 38 | ployeeNumber $ employeeType $ givenName $ homePhone $ homePostalAddress $ ini 39 | tials $ jpegPhoto $ labeledURI $ mail $ manager $ mobile $ o $ pager $ photo 40 | $ roomNumber $ secretary $ uid $ userCertificate $ x500uniqueIdentifier $ pre 41 | ferredLanguage $ userSMIMECertificate $ userPKCS12 ) ) 42 | structuralObjectClass: olcSchemaConfig 43 | entryUUID: 2964f060-6754-1033-8d4f-1703270f04bd 44 | creatorsName: cn=admin,cn=config 45 | createTimestamp: 20140503211805Z 46 | entryCSN: 20140503211805.663817Z#000000#000#000000 47 | modifiersName: cn=admin,cn=config 48 | modifyTimestamp: 20140503211805Z 49 | -------------------------------------------------------------------------------- /tests/test_env/etc/ldap/slapd.d/cn=config/olcDatabase={-1}frontend.ldif: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify. 2 | # CRC32 e8f0a13c 3 | dn: olcDatabase={-1}frontend 4 | objectClass: olcDatabaseConfig 5 | objectClass: olcFrontendConfig 6 | olcDatabase: {-1}frontend 7 | olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external 8 | ,cn=auth manage by * break 9 | olcAccess: {1}to dn.exact="" by * read 10 | olcAccess: {2}to dn.base="cn=Subschema" by * read 11 | olcSizeLimit: 500 12 | structuralObjectClass: olcDatabaseConfig 13 | entryUUID: 38579ebe-750a-103e-848a-9578878139e2 14 | creatorsName: cn=config 15 | createTimestamp: 20240312221838Z 16 | entryCSN: 20240312221838.644978Z#000000#000#000000 17 | modifiersName: cn=config 18 | modifyTimestamp: 20240312221838Z 19 | -------------------------------------------------------------------------------- /tests/test_env/etc/ldap/slapd.d/cn=config/olcDatabase={0}config.ldif: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify. 2 | # CRC32 caed0a01 3 | dn: olcDatabase={0}config 4 | objectClass: olcDatabaseConfig 5 | olcDatabase: {0}config 6 | olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external 7 | ,cn=auth manage by * break 8 | olcRootDN: cn=admin,cn=config 9 | structuralObjectClass: olcDatabaseConfig 10 | entryUUID: 3857a274-750a-103e-848b-9578878139e2 11 | creatorsName: cn=config 12 | createTimestamp: 20240312221838Z 13 | entryCSN: 20240312221838.645074Z#000000#000#000000 14 | modifiersName: cn=config 15 | modifyTimestamp: 20240312221838Z 16 | -------------------------------------------------------------------------------- /tests/test_env/etc/ldap/slapd.d/cn=config/olcDatabase={1}mdb.ldif: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify. 2 | # CRC32 91d29b33 3 | dn: olcDatabase={1}mdb 4 | objectClass: olcDatabaseConfig 5 | objectClass: olcMdbConfig 6 | olcDatabase: {1}mdb 7 | olcDbDirectory: /var/lib/ldap 8 | olcSuffix: dc=example,dc=org 9 | olcAccess: {0}to attrs=userPassword,shadowLastChange by self write by anonymou 10 | s auth by dn="cn=admin,dc=example,dc=org" write by dn="cn=ldapcherry,dc=example,dc=org" write by dn="cn=dnscherry,dc=example,dc=org" write by * none 11 | olcAccess: {1}to dn.base="" by * read 12 | olcAccess: {2}to * by self write by dn="cn=dnscherry,dc=example,dc=org" write by * read 13 | olcAccess: {3}to * by self write by dn="cn=ldapcherry,dc=example,dc=org" write by * read 14 | olcLastMod: TRUE 15 | olcRootDN: cn=admin,dc=example,dc=org 16 | olcRootPW: {SSHA}Fp+rSxe5eFsj0DGITJts4DwdSDFDZG9P 17 | olcDbCheckpoint: 512 30 18 | olcDbIndex: objectClass eq 19 | olcDbIndex: cn,uid eq 20 | olcDbIndex: uidNumber,gidNumber eq 21 | olcDbIndex: member,memberUid eq 22 | olcDbMaxSize: 1073741824 23 | structuralObjectClass: olcMdbConfig 24 | entryUUID: 3857d7ee-750a-103e-8492-9578878139e2 25 | creatorsName: cn=admin,cn=config 26 | createTimestamp: 20240312221838Z 27 | entryCSN: 20240312221838.646442Z#000000#000#000000 28 | modifiersName: cn=admin,cn=config 29 | modifyTimestamp: 20240312221838Z 30 | -------------------------------------------------------------------------------- /tests/test_env/etc/ldap/ssl/TEST-cacert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIExzCCA6+gAwIBAgIJALS1NAXjfh/yMA0GCSqGSIb3DQEBBQUAMHYxCzAJBgNV 3 | BAYTAlRTMQ0wCwYDVQQIEwRURVNUMQ0wCwYDVQQHEwRURVNUMQ0wCwYDVQQKEwRU 4 | RVNUMQ0wCwYDVQQLEwRURVNUMQ0wCwYDVQQDEwRURVNUMRwwGgYJKoZIhvcNAQkB 5 | Fg1URVNUQFRFU1Qub3JnMCAXDTE0MDUxMzE3MDYyOVoYDzIxNDEwOTA0MTcwNjI5 6 | WjB2MQswCQYDVQQGEwJUUzENMAsGA1UECBMEVEVTVDENMAsGA1UEBxMEVEVTVDEN 7 | MAsGA1UEChMEVEVTVDENMAsGA1UECxMEVEVTVDENMAsGA1UEAxMEVEVTVDEcMBoG 8 | CSqGSIb3DQEJARYNVEVTVEBURVNULm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEP 9 | ADCCAQoCggEBAJbvf9M6S8Ml/asTcn9CA8n7Wpil7GQcdRE5Gs92/EIlKQAAqFl2 10 | 5ABLPmHQRrERa4A4dymJUrrTPT5fM4Lzr/tq9fQ8tZVWv6xrceSxc7KQmDAD828t 11 | e/S9SWimriAGKUXxK+DGQUDq71H82Eccv1FJiLcWVFuSOz7dGvX0bvxGfQ3AAa/G 12 | qhVPAa0u4cPiu8Z2S5vH8qaKliN87Jt2yvTvyU9oIrdbIJ10AFp1v1qNde7j/2Jo 13 | F9WQbAm9961zgnIU0rsTuLs79Wv6mXGkaNQVgGGEqvoOLbvKKIkd6aVvP0cI7kE/ 14 | qhPWDTCd/wnORyGFJC7lWhUU+HsefUjIEisCAwEAAaOCAVQwggFQMB0GA1UdDgQW 15 | BBQnonzDDjK9iLHMCrmrQH2mQrmmkTCBqAYDVR0jBIGgMIGdgBQnonzDDjK9iLHM 16 | CrmrQH2mQrmmkaF6pHgwdjELMAkGA1UEBhMCVFMxDTALBgNVBAgTBFRFU1QxDTAL 17 | BgNVBAcTBFRFU1QxDTALBgNVBAoTBFRFU1QxDTALBgNVBAsTBFRFU1QxDTALBgNV 18 | BAMTBFRFU1QxHDAaBgkqhkiG9w0BCQEWDVRFU1RAVEVTVC5vcmeCCQC0tTQF434f 19 | 8jAPBgNVHRMBAf8EBTADAQH/MBEGCWCGSAGG+EIBAQQEAwIBBjAJBgNVHRIEAjAA 20 | MCsGCWCGSAGG+EIBDQQeFhxUaW55Q0EgR2VuZXJhdGVkIENlcnRpZmljYXRlMBgG 21 | A1UdEQQRMA+BDVRFU1RAVEVTVC5vcmcwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 22 | DQEBBQUAA4IBAQBNpHHihymOhVPRo1PVICaH4CTCL8Pk257TYAG5WT3PEz/Mhye7 23 | iDxi/fVCI5Zs/FAq7MPNEwEFxH0i0fy97v/5N3i3QjE8iIX57U7QcV3/Nh1DywYH 24 | ltBAZr1rA6hZQj7F6JigtudIY12u8+atC44S8W4Esc+JYNJK5xqhO8MUwqcgH8lh 25 | cBsxlhF4o7J2LQUZWSILN67NoZsz3ucWbjdbT+zLjjeO/xc1GV9a+nKRSXDLPxop 26 | aMcseuRBZQOs2YEiu5Arm3Q6DDmpKRnbJzKa32HHL2bZjgE9ddfTHWuPOk896oOI 27 | Jv78fn+aEi5uIM/hemtxwnfN7xMFwZPsHH5N 28 | -----END CERTIFICATE----- 29 | -------------------------------------------------------------------------------- /tests/test_env/etc/ldap/ssl/ldap@dnscherry.org-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIF0TCCBLmgAwIBAgIBATANBgkqhkiG9w0BAQUFADB2MQswCQYDVQQGEwJUUzEN 3 | MAsGA1UECBMEVEVTVDENMAsGA1UEBxMEVEVTVDENMAsGA1UEChMEVEVTVDENMAsG 4 | A1UECxMEVEVTVDENMAsGA1UEAxMEVEVTVDEcMBoGCSqGSIb3DQEJARYNVEVTVEBU 5 | RVNULm9yZzAgFw0xNDA1MTMxNzEwMTFaGA8yMTE0MDQxOTE3MTAxMVowgYkxCzAJ 6 | BgNVBAYTAlRTMQ0wCwYDVQQIEwRURVNUMQ0wCwYDVQQHEwRURVNUMQ0wCwYDVQQK 7 | EwRURVNUMQ0wCwYDVQQLEwRURVNUMRswGQYDVQQDExJsZGFwLmRuc2NoZXJyeS5v 8 | cmcxITAfBgkqhkiG9w0BCQEWEmxkYXBAZG5zY2hlcnJ5Lm9yZzCCAiIwDQYJKoZI 9 | hvcNAQEBBQADggIPADCCAgoCggIBAMp917+WTVimG2Mz2pSwwxNqJKUMpLuD7h80 10 | EtOMNAtM3Uaso9aYFYP44QUzsSwiV0ITcVJCp/XXKbFlwamPXq0/MU/2ndjQ22Eh 11 | wn0uDH+TUu8lPQ/DSnB+DxeJgTs0+o6kaOH3bdzhMuxa2hTiO3sxu/MubaQ5B+Rp 12 | 87k8pMd0go5yaWBvrMQmMwrBqpF20g1sGhMbv6Jzj86RLBeL54lY7XMMPC3yrWBK 13 | C+lMsLqnXElfWxea9zYSgAJQ9j0azvhDMaBmDpT6YD3dT9Bd7mlLlKSK3QHPwSHR 14 | nd6PSGlJAtVWdMsZNlYRiuhL0I9aVX8CcsQTkf2ZWR5HIh1LLbN1cyzRmgSXDQsz 15 | dwSXklX6Q+GtE82Ev2f4hfYMOZgJwC7kkYzqyXRlcCHIBu0dS4HCAXUeJWqhmHNE 16 | MxqvsEZiOixlnVUctV9mR3jLFpaljNBSgy2x+4yZznHZeGPNA7GWI2Yc71k6Gb5g 17 | zqvhjYdlA+yyjsBLhAFqwFlrUD+h1m0wtdPRY4tETQLvXnn9Eke9PlVvya2U8pMN 18 | ECVe8qzXfxlXEmRg+KMiC12QAj/Ix1B08U1Dm2i6zC7GySEo1L0Nk19evYijOwMm 19 | GHJfstD6pffsTJLWtZiznJWiqy1bF1y1W6Tz3nXzjmr121k3WzbrIN1AevZGX0Nq 20 | xs7E0pMBAgMBAAGjggFSMIIBTjAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIG 21 | QDArBglghkgBhvhCAQ0EHhYcVGlueUNBIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAd 22 | BgNVHQ4EFgQUU9X8RU3rfmhHs5/qnA6HWSxo0JMwgagGA1UdIwSBoDCBnYAUJ6J8 23 | ww4yvYixzAq5q0B9pkK5ppGheqR4MHYxCzAJBgNVBAYTAlRTMQ0wCwYDVQQIEwRU 24 | RVNUMQ0wCwYDVQQHEwRURVNUMQ0wCwYDVQQKEwRURVNUMQ0wCwYDVQQLEwRURVNU 25 | MQ0wCwYDVQQDEwRURVNUMRwwGgYJKoZIhvcNAQkBFg1URVNUQFRFU1Qub3JnggkA 26 | tLU0BeN+H/IwGAYDVR0SBBEwD4ENVEVTVEBURVNULm9yZzAdBgNVHREEFjAUgRJs 27 | ZGFwQGRuc2NoZXJyeS5vcmcwDQYJKoZIhvcNAQEFBQADggEBAG5m16ZZxllqkEbo 28 | qwqg4nMyi6zejRf3Y1Bi5xe6nA+SPn5IHCOrl70S7/s7WZkvYizOoGaquXl+Ql0Z 29 | 2xImhBxL6FE/ehZeqhf9yR2qQrSZrky9cnEQqZfEdap2MdUDi1J8up4gcI94TCaP 30 | fARMuJB7doKUDu0QXJKSMbteo22M2YDcnyveDWjRnSx1jccsqcdc03RVDLw4q3GW 31 | et+h6B52UEK2gCHZh9TUi8g3050Hb+gO7A4BoqPfnLEyLlvcIG8Vruy69EZapTcb 32 | nPmKAPwia9XjABb75o2bho5Pq0EB5OcnalNVbQJ3PR6Jd6Ly/k0Mf7BKyk1DzMq6 33 | qJF4L9M= 34 | -----END CERTIFICATE----- 35 | -------------------------------------------------------------------------------- /tests/test_env/etc/ldapcherry/TEST-cacert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIExzCCA6+gAwIBAgIJALS1NAXjfh/yMA0GCSqGSIb3DQEBBQUAMHYxCzAJBgNV 3 | BAYTAlRTMQ0wCwYDVQQIEwRURVNUMQ0wCwYDVQQHEwRURVNUMQ0wCwYDVQQKEwRU 4 | RVNUMQ0wCwYDVQQLEwRURVNUMQ0wCwYDVQQDEwRURVNUMRwwGgYJKoZIhvcNAQkB 5 | Fg1URVNUQFRFU1Qub3JnMCAXDTE0MDUxMzE3MDYyOVoYDzIxNDEwOTA0MTcwNjI5 6 | WjB2MQswCQYDVQQGEwJUUzENMAsGA1UECBMEVEVTVDENMAsGA1UEBxMEVEVTVDEN 7 | MAsGA1UEChMEVEVTVDENMAsGA1UECxMEVEVTVDENMAsGA1UEAxMEVEVTVDEcMBoG 8 | CSqGSIb3DQEJARYNVEVTVEBURVNULm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEP 9 | ADCCAQoCggEBAJbvf9M6S8Ml/asTcn9CA8n7Wpil7GQcdRE5Gs92/EIlKQAAqFl2 10 | 5ABLPmHQRrERa4A4dymJUrrTPT5fM4Lzr/tq9fQ8tZVWv6xrceSxc7KQmDAD828t 11 | e/S9SWimriAGKUXxK+DGQUDq71H82Eccv1FJiLcWVFuSOz7dGvX0bvxGfQ3AAa/G 12 | qhVPAa0u4cPiu8Z2S5vH8qaKliN87Jt2yvTvyU9oIrdbIJ10AFp1v1qNde7j/2Jo 13 | F9WQbAm9961zgnIU0rsTuLs79Wv6mXGkaNQVgGGEqvoOLbvKKIkd6aVvP0cI7kE/ 14 | qhPWDTCd/wnORyGFJC7lWhUU+HsefUjIEisCAwEAAaOCAVQwggFQMB0GA1UdDgQW 15 | BBQnonzDDjK9iLHMCrmrQH2mQrmmkTCBqAYDVR0jBIGgMIGdgBQnonzDDjK9iLHM 16 | CrmrQH2mQrmmkaF6pHgwdjELMAkGA1UEBhMCVFMxDTALBgNVBAgTBFRFU1QxDTAL 17 | BgNVBAcTBFRFU1QxDTALBgNVBAoTBFRFU1QxDTALBgNVBAsTBFRFU1QxDTALBgNV 18 | BAMTBFRFU1QxHDAaBgkqhkiG9w0BCQEWDVRFU1RAVEVTVC5vcmeCCQC0tTQF434f 19 | 8jAPBgNVHRMBAf8EBTADAQH/MBEGCWCGSAGG+EIBAQQEAwIBBjAJBgNVHRIEAjAA 20 | MCsGCWCGSAGG+EIBDQQeFhxUaW55Q0EgR2VuZXJhdGVkIENlcnRpZmljYXRlMBgG 21 | A1UdEQQRMA+BDVRFU1RAVEVTVC5vcmcwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 22 | DQEBBQUAA4IBAQBNpHHihymOhVPRo1PVICaH4CTCL8Pk257TYAG5WT3PEz/Mhye7 23 | iDxi/fVCI5Zs/FAq7MPNEwEFxH0i0fy97v/5N3i3QjE8iIX57U7QcV3/Nh1DywYH 24 | ltBAZr1rA6hZQj7F6JigtudIY12u8+atC44S8W4Esc+JYNJK5xqhO8MUwqcgH8lh 25 | cBsxlhF4o7J2LQUZWSILN67NoZsz3ucWbjdbT+zLjjeO/xc1GV9a+nKRSXDLPxop 26 | aMcseuRBZQOs2YEiu5Arm3Q6DDmpKRnbJzKa32HHL2bZjgE9ddfTHWuPOk896oOI 27 | Jv78fn+aEi5uIM/hemtxwnfN7xMFwZPsHH5N 28 | -----END CERTIFICATE----- 29 | -------------------------------------------------------------------------------- /tests/test_env/etc/ldapcherry/attributes.yml: -------------------------------------------------------------------------------- 1 | cn: 2 | description: "First Name and Display Name" 3 | display_name: "Display Name" 4 | type: string 5 | weight: 30 6 | autofill: 7 | function: lcDisplayName 8 | args: 9 | - $first-name 10 | - $name 11 | backends: 12 | ldap: cn 13 | ad: cn 14 | first-name: 15 | description: "First name of the user" 16 | display_name: "First Name" 17 | search_displayed: True 18 | type: string 19 | weight: 20 20 | backends: 21 | ldap: givenName 22 | ad: givenName 23 | name: 24 | description: "Family name of the user" 25 | display_name: "Name" 26 | search_displayed: True 27 | weight: 10 28 | type: string 29 | backends: 30 | ldap: sn 31 | ad: sn 32 | email: 33 | description: "Email of the user" 34 | display_name: "Email" 35 | search_displayed: True 36 | type: email 37 | weight: 40 38 | autofill: 39 | function: lcMail 40 | args: 41 | - $first-name 42 | - $name 43 | - '@example.com' 44 | backends: 45 | ldap: mail 46 | ad: mail 47 | uid: 48 | description: "UID of the user" 49 | display_name: "UID" 50 | search_displayed: True 51 | key: True 52 | type: string 53 | weight: 50 54 | autofill: 55 | function: lcUid 56 | args: 57 | - $first-name 58 | - $name 59 | - '10000' 60 | - '40000' 61 | backends: 62 | ldap: uid 63 | ad: sAMAccountName 64 | uidNumber: 65 | description: "User ID Number of the user" 66 | display_name: "UID Number" 67 | weight: 60 68 | type: int 69 | autofill: 70 | function: lcUidNumber 71 | args: 72 | - $first-name 73 | - $name 74 | - '10000' 75 | - '40000' 76 | backends: 77 | ldap: uidNumber 78 | ad: uidNumber 79 | gidNumber: 80 | description: "Group ID Number of the user" 81 | display_name: "GID Number" 82 | weight: 70 83 | type: int 84 | default: '10000' 85 | backends: 86 | ldap: gidNumber 87 | ad: gidNumber 88 | shell: 89 | description: "Shell of the user" 90 | display_name: "Shell" 91 | weight: 80 92 | self: True 93 | type: stringlist 94 | values: 95 | - /bin/bash 96 | - /bin/zsh 97 | - /bin/sh 98 | backends: 99 | ldap: loginShell 100 | ad: loginShell 101 | home: 102 | description: "Home user path" 103 | display_name: "Home" 104 | weight: 90 105 | type: string 106 | autofill: 107 | function: lcHomeDir 108 | args: 109 | - $first-name 110 | - $name 111 | - /home/ 112 | backends: 113 | ldap: homeDirectory 114 | ad: homeDirectory 115 | password: 116 | description: "Password of the user" 117 | display_name: "Password" 118 | weight: 31 119 | self: True 120 | type: password 121 | backends: 122 | ldap: userPassword 123 | ad: unicodePwd 124 | 125 | #logscript: 126 | # description: "Windows login script" 127 | # display_name: "Login script" 128 | # weight: 100 129 | # type: fix 130 | # value: login1.bat 131 | # backends: 132 | # ad: scriptPath 133 | -------------------------------------------------------------------------------- /tests/test_env/etc/ldapcherry/ldapcherry.ini: -------------------------------------------------------------------------------- 1 | # global parameters 2 | [global] 3 | 4 | # listing interface 5 | server.socket_host = '127.0.0.1' 6 | # port 7 | server.socket_port = 8080 8 | # number of threads 9 | server.thread_pool = 8 10 | #don't show traceback on error 11 | request.show_tracebacks = False 12 | 13 | # log configuration 14 | # /!\ you can't have multiple log handlers 15 | ##################################### 16 | # configuration to log in files # 17 | ##################################### 18 | ## logger 'file' for access log 19 | #log.access_handler = 'file' 20 | ## logger syslog for error and ldapcherry log 21 | #log.error_handler = 'file' 22 | ## access log file 23 | #log.access_file = '/tmp/ldapcherry_access.log' 24 | ## error and ldapcherry log file 25 | #log.error_file = '/tmp/ldapcherry_error.log' 26 | 27 | ##################################### 28 | # configuration to log in syslog # 29 | ##################################### 30 | # logger syslog for access log 31 | #log.access_handler = 'syslog' 32 | ## logger syslog for error and ldapcherry log 33 | log.error_handler = 'syslog' 34 | 35 | ##################################### 36 | # configuration to not log at all # 37 | ##################################### 38 | # logger none for access log 39 | log.access_handler = 'syslog' 40 | # logger none for error and ldapcherry log 41 | #log.error_handler = 'none' 42 | 43 | # log level 44 | log.level = 'debug' 45 | 46 | # session configuration 47 | # activate session 48 | tools.sessions.on = True 49 | # session timeout 50 | tools.sessions.timeout = 10 51 | # file session storage(to use if multiple processes, 52 | # default is in RAM and per process) 53 | #tools.sessions.storage_type = "file" 54 | # session 55 | #tools.sessions.storage_path = "/var/lib/ldapcherry/sessions" 56 | 57 | [attributes] 58 | 59 | # file discribing form content 60 | attributes.file = '/etc/ldapcherry/attributes.yml' 61 | 62 | [roles] 63 | 64 | # file listing roles 65 | roles.file = '/etc/ldapcherry/roles.yml' 66 | 67 | [backends] 68 | 69 | ldap.module = 'ldapcherry.backend.backendLdap' 70 | ldap.groupdn = 'ou=Group,dc=example,dc=org' 71 | ldap.userdn = 'ou=people,dc=example,dc=org' 72 | ldap.binddn = 'cn=dnscherry,dc=example,dc=org' 73 | ldap.password = 'password' 74 | ldap.uri = 'ldap://ldap.ldapcherry.org:390' 75 | ldap.ca = '/etc/dnscherry/TEST-cacert.pem' 76 | ldap.starttls = 'off' 77 | ldap.checkcert = 'off' 78 | ldap.user_filter_tmpl = '(uid=%(username)s)' 79 | ldap.group_filter_tmpl = '(member=%(userdn)s)' 80 | ldap.search_filter_tmpl = '(|(uid=%(searchstring)s*)(sn=%(searchstring)s*))' 81 | ldap.group_attr.member = "%(dn)s" 82 | 83 | #ldap.objectclasses = 'top, person, organizationalPerson, user' 84 | ldap.objectclasses = 'top, person, posixAccount, inetOrgPerson' 85 | ldap.dn_user_attr = 'uid' 86 | ldap.timeout = 1 87 | 88 | ad.module = 'ldapcherry.backend.backendAD' 89 | ad.domain = 'dc.ldapcherry.org' 90 | ad.login = 'administrator' 91 | ad.password = 'qwertyP455' 92 | ad.uri = 'ldaps://ldap.ldapcherry.org:636' 93 | ad.checkcert = 'off' 94 | 95 | # authentification parameters 96 | [auth] 97 | 98 | # Auth mode 99 | # * and: user must authenticate on all backends 100 | # * or: user must authenticate on one of the backend 101 | # * none: disable authentification 102 | # * custom: custom authentification module (need auth.module param) 103 | auth.mode = 'none' 104 | 105 | # custom auth module to load 106 | #auth.module = 'ldapcherry.auth.modNone' 107 | 108 | [ppolicy] 109 | 110 | # password policy module 111 | ppolicy.module = 'ldapcherry.ppolicy.simple' 112 | 113 | # parameters of the module 114 | min_length = 2 115 | min_upper = 0 116 | min_digit = 0 117 | 118 | # resources parameters 119 | [resources] 120 | # templates directory 121 | templates.dir = './resources/templates/' 122 | 123 | [/static] 124 | tools.staticdir.on = True 125 | tools.staticdir.dir = './resources/static/' 126 | -------------------------------------------------------------------------------- /tests/test_env/etc/ldapcherry/roles.yml: -------------------------------------------------------------------------------- 1 | admin-lv3: 2 | display_name: Administrators Level 3 3 | description: description 4 | backends_groups: 5 | ldap: 6 | - cn=dns admins,ou=Group,dc=example,dc=org 7 | - cn=nagios admins,ou=Group,dc=example,dc=org 8 | - cn=puppet admins,ou=Group,dc=example,dc=org 9 | - cn=users,ou=Group,dc=example,dc=org 10 | ad: 11 | - Administrators 12 | - Domain Controllers 13 | 14 | admin-lv2: 15 | display_name: Administrators Level 2 16 | description: description 17 | LC_admins: True 18 | backends_groups: 19 | ldap: 20 | - cn=nagios admins,ou=Group,dc=example,dc=org 21 | - cn=users,ou=Group,dc=example,dc=org 22 | 23 | developers: 24 | display_name: Developpers 25 | description: description 26 | backends_groups: 27 | ldap: 28 | - cn=developers,ou=Group,dc=example,dc=org 29 | - cn=users,ou=Group,dc=example,dc=org 30 | 31 | users: 32 | display_name: Simple Users 33 | description: description 34 | backends_groups: 35 | ldap: 36 | - cn=users,ou=Group,dc=example,dc=org 37 | -------------------------------------------------------------------------------- /tests/test_env/etc/ldapcherry/users.db: -------------------------------------------------------------------------------- 1 | test:$apr1$oZW1susE$Md2rVNXgeOecm188fqqGx. 2 | --------------------------------------------------------------------------------