From 0124a33e6c4e894c464cadbff826079a1d0121a9 Mon Sep 17 00:00:00 2001 From: alexferl Date: Wed, 3 Apr 2024 14:26:56 -0400 Subject: [PATCH 1/5] update project, merge PRs Signed-off-by: alexferl --- .editorconfig | 16 + .github/workflows/test.yaml | 71 +++ .pre-commit-config.yaml | 34 +- .travis.yml | 15 - AUTHORS | 8 - Makefile | 47 +- Pipfile | 13 + Pipfile.lock | 374 ++++++++++++ README.md | 32 +- VERSION | 1 + dev_requirements.txt | 6 - docs/Makefile | 177 ------ docs/_themes/LICENSE | 37 -- docs/_themes/README | 31 - docs/_themes/flask/layout.html | 24 - docs/_themes/flask/relations.html | 19 - docs/_themes/flask/static/flasky.css_t | 577 ------------------- docs/_themes/flask/theme.conf | 9 - docs/_themes/flask_small/layout.html | 22 - docs/_themes/flask_small/static/flasky.css_t | 287 --------- docs/_themes/flask_small/theme.conf | 10 - docs/_themes/flask_theme_support.py | 89 --- docs/conf.py | 286 --------- docs/index.rst | 206 ------- examples/basic_auth/app.py | 4 +- examples/basic_auth/app_oldap.py | 8 +- examples/blueprints/blueprints/config.py | 8 +- examples/groups/app_oldap.py | 6 +- flask_simpleldap/__init__.py | 66 +-- pyproject.toml | 3 + requirements-dev.txt | 19 + requirements.txt | 13 +- setup.cfg | 29 + setup.py | 40 -- tests/__init__.py | 0 tests/test_simpleldap.py | 32 + 36 files changed, 659 insertions(+), 1960 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/workflows/test.yaml delete mode 100644 .travis.yml delete mode 100644 AUTHORS create mode 100644 Pipfile create mode 100644 Pipfile.lock create mode 100644 VERSION delete mode 100644 dev_requirements.txt delete mode 100644 docs/Makefile delete mode 100644 docs/_themes/LICENSE delete mode 100644 docs/_themes/README delete mode 100644 docs/_themes/flask/layout.html delete mode 100644 docs/_themes/flask/relations.html delete mode 100644 docs/_themes/flask/static/flasky.css_t delete mode 100644 docs/_themes/flask/theme.conf delete mode 100644 docs/_themes/flask_small/layout.html delete mode 100644 docs/_themes/flask_small/static/flasky.css_t delete mode 100644 docs/_themes/flask_small/theme.conf delete mode 100644 docs/_themes/flask_theme_support.py delete mode 100644 docs/conf.py delete mode 100644 docs/index.rst create mode 100644 pyproject.toml create mode 100644 requirements-dev.txt create mode 100644 setup.cfg delete mode 100644 setup.py create mode 100644 tests/__init__.py create mode 100644 tests/test_simpleldap.py diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..15a145a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[*.py] +indent_size = 4 + +[Makefile] +indent_style = tab +indent_size = 4 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..98f942b --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,71 @@ +name: Test +on: + push: + branches: + - master + pull_request: + branches: + - master +jobs: + build: + runs-on: '${{ matrix.os }}' + strategy: + matrix: + include: + - os: ubuntu-latest + python-version: '3.8' + FLASK: 3.0.2 + - os: ubuntu-latest + python-version: '3.8' + FLASK: 2.3.3 + - os: ubuntu-latest + python-version: '3.8' + FLASK: 2.2.5 + - os: ubuntu-latest + python-version: '3.9' + FLASK: 3.0.2 + - os: ubuntu-latest + python-version: '3.9' + FLASK: 2.3.3 + - os: ubuntu-latest + python-version: '3.9' + FLASK: 2.2.5 + - os: ubuntu-latest + python-version: '3.10' + FLASK: 3.0.2 + - os: ubuntu-latest + python-version: '3.10' + FLASK: 2.3.3 + - os: ubuntu-latest + python-version: '3.10' + FLASK: 2.2.5 + - os: ubuntu-latest + python-version: '3.11' + FLASK: 3.0.2 + - os: ubuntu-latest + python-version: '3.11' + FLASK: 2.3.3 + - os: ubuntu-latest + python-version: '3.11' + FLASK: 2.2.5 + - os: ubuntu-latest + python-version: '3.12' + FLASK: 3.0.2 + - os: ubuntu-latest + python-version: '3.12' + FLASK: 2.3.3 + - os: ubuntu-latest + python-version: '3.12' + FLASK: 2.2.5 + steps: + - name: 'Set up Python ${{ matrix.python-version }}' + uses: actions/setup-python@v2 + with: + python-version: '${{ matrix.python-version }}' + - uses: actions/checkout@v2 + - run: pip install pytest Flask==$FLASK + env: + FLASK: '${{ matrix.FLASK }}' + - run: pip install -r requirements-dev.txt . + - run: pip install . + - run: pytest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c74020e..fe8e9bf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,15 +1,21 @@ repos: -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.1.0 - hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: mixed-line-ending - args: ['--fix=lf'] - description: Forces to replace line ending by the UNIX 'lf' character. -- repo: https://github.com/psf/black - rev: 22.1.0 - hooks: - - id: black - language_version: python3 - args: [-t, py310] + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: mixed-line-ending + args: ['--fix=lf'] + description: Forces to replace line ending by the UNIX 'lf' character. + - repo: https://github.com/psf/black + rev: 24.3.0 + hooks: + - id: black + language_version: python3 + - repo: https://github.com/d-ryzhykau/pipenv-lock-pre-commit + rev: 0.5.0 + hooks: + - id: pipenv-lock + - id: pipenv-verify + - id: pipenv-requirements + - id: pipenv-requirements-dev diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2ddc001..0000000 --- a/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: python -sudo: required -dist: focal -python: - - "3.7" - - "3.8" - - "3.9" -env: - - FLASK=2.0.2 - - FLASK=1.1.4 - - FLASK=1.0.4 -install: - - pip install Flask==$FLASK - - pip install -r dev_requirements.txt -script: python setup.py test diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index 0850a48..0000000 --- a/AUTHORS +++ /dev/null @@ -1,8 +0,0 @@ -================================== - AUTHORS (in chronological order) -================================== - -Alexandre Ferland (admiralobvious) -Chris Seymour (iiSeymour) -Timothy Allen (OptiverTimAll) -Jose Manuel (jm66) diff --git a/Makefile b/Makefile index 16ef367..291af74 100644 --- a/Makefile +++ b/Makefile @@ -1,47 +1,24 @@ -.PHONY: help dev clean update test lint pre-commit - -VENV_NAME?=venv -VENV_ACTIVATE=. $(VENV_NAME)/bin/activate -PYTHON=${VENV_NAME}/bin/python3 +.PHONY: help dev test lint pre-commit .DEFAULT: help help: @echo "make dev" - @echo " prepare development environment, use only once" - @echo "make clean" - @echo " delete development environment" - @echo "make update" - @echo " update dependencies" + @echo " prepare development environment" @echo "make test" - @echo " run tests" + @echo " run tests" @echo "make lint" - @echo " run black" + @echo " run black" @echo "make pre-commit" - @echo " run pre-commit hooks" + @echo " run pre-commit hooks" dev: - make venv - -venv: $(VENV_NAME)/bin/activate -$(VENV_NAME)/bin/activate: - test -d $(VENV_NAME) || virtualenv -p python3 $(VENV_NAME) - ${PYTHON} -m pip install -U pip - ${PYTHON} -m pip install -r dev_requirements.txt - $(VENV_NAME)/bin/pre-commit install - touch $(VENV_NAME)/bin/activate - -clean: - rm -rf venv - -update: - ${PYTHON} -m pip install -U -r dev_requirements.txt - $(VENV_NAME)/bin/pre-commit install + pipenv install --dev -test: venv - ${PYTHON} -m pytest +test: + pipenv run pytest -lint: venv - $(VENV_NAME)/bin/black -t py310 --exclude $(VENV_NAME) . +lint: + pipenv run black . -pre-commit: venv - $(VENV_NAME)/bin/pre-commit +pre-commit: + pipenv run pre-commit diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..4113b29 --- /dev/null +++ b/Pipfile @@ -0,0 +1,13 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +flask = "==3.0.2" +python-ldap = "==3.4.4" + +[dev-packages] +black = "==24.3.0" +pre-commit = "==3.5.0" +pytest = "==8.1.1" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..f8328c1 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,374 @@ +{ + "_meta": { + "hash": { + "sha256": "5fbd42bf4fcec6b59a08e107f90dca4cb5bb8a9312a2234733e7d413cce97960" + }, + "pipfile-spec": 6, + "requires": {}, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "blinker": { + "hashes": [ + "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", + "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182" + ], + "markers": "python_version >= '3.8'", + "version": "==1.7.0" + }, + "click": { + "hashes": [ + "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" + ], + "markers": "python_version >= '3.7'", + "version": "==8.1.7" + }, + "flask": { + "hashes": [ + "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e", + "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==3.0.2" + }, + "itsdangerous": { + "hashes": [ + "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", + "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" + ], + "markers": "python_version >= '3.7'", + "version": "==2.1.2" + }, + "jinja2": { + "hashes": [ + "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa", + "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90" + ], + "markers": "python_version >= '3.7'", + "version": "==3.1.3" + }, + "markupsafe": { + "hashes": [ + "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf", + "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff", + "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f", + "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3", + "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532", + "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f", + "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617", + "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df", + "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4", + "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906", + "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f", + "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4", + "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8", + "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371", + "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2", + "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465", + "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52", + "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6", + "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169", + "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad", + "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2", + "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0", + "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029", + "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f", + "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a", + "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced", + "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5", + "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c", + "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf", + "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9", + "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb", + "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad", + "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3", + "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1", + "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46", + "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc", + "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a", + "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee", + "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900", + "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5", + "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea", + "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f", + "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5", + "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e", + "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a", + "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f", + "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50", + "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a", + "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", + "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4", + "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff", + "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2", + "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46", + "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b", + "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf", + "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5", + "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5", + "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab", + "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd", + "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68" + ], + "markers": "python_version >= '3.7'", + "version": "==2.1.5" + }, + "pyasn1": { + "hashes": [ + "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c", + "sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473" + ], + "markers": "python_version >= '3.8'", + "version": "==0.6.0" + }, + "pyasn1-modules": { + "hashes": [ + "sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6", + "sha256:be04f15b66c206eed667e0bb5ab27e2b1855ea54a842e5037738099e8ca4ae0b" + ], + "markers": "python_version >= '3.8'", + "version": "==0.4.0" + }, + "python-ldap": { + "hashes": [ + "sha256:7edb0accec4e037797705f3a05cbf36a9fde50d08c8f67f2aef99a2628fab828" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==3.4.4" + }, + "werkzeug": { + "hashes": [ + "sha256:3aac3f5da756f93030740bc235d3e09449efcf65f2f55e3602e1d851b8f48795", + "sha256:e39b645a6ac92822588e7b39a692e7828724ceae0b0d702ef96701f90e70128d" + ], + "markers": "python_version >= '3.8'", + "version": "==3.0.2" + } + }, + "develop": { + "black": { + "hashes": [ + "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f", + "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93", + "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11", + "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0", + "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9", + "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5", + "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213", + "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d", + "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7", + "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837", + "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f", + "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395", + "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995", + "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f", + "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597", + "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959", + "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5", + "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb", + "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4", + "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7", + "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd", + "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==24.3.0" + }, + "cfgv": { + "hashes": [ + "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", + "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560" + ], + "markers": "python_version >= '3.8'", + "version": "==3.4.0" + }, + "click": { + "hashes": [ + "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" + ], + "markers": "python_version >= '3.7'", + "version": "==8.1.7" + }, + "distlib": { + "hashes": [ + "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784", + "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64" + ], + "version": "==0.3.8" + }, + "filelock": { + "hashes": [ + "sha256:5ffa845303983e7a0b7ae17636509bc97997d58afeafa72fb141a17b152284cb", + "sha256:a79895a25bbefdf55d1a2a0a80968f7dbb28edcd6d4234a0afb3f37ecde4b546" + ], + "markers": "python_version >= '3.8'", + "version": "==3.13.3" + }, + "identify": { + "hashes": [ + "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791", + "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e" + ], + "markers": "python_version >= '3.8'", + "version": "==2.5.35" + }, + "iniconfig": { + "hashes": [ + "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", + "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.0" + }, + "mypy-extensions": { + "hashes": [ + "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", + "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782" + ], + "markers": "python_version >= '3.5'", + "version": "==1.0.0" + }, + "nodeenv": { + "hashes": [ + "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2", + "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", + "version": "==1.8.0" + }, + "packaging": { + "hashes": [ + "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5", + "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9" + ], + "markers": "python_version >= '3.7'", + "version": "==24.0" + }, + "pathspec": { + "hashes": [ + "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", + "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712" + ], + "markers": "python_version >= '3.8'", + "version": "==0.12.1" + }, + "platformdirs": { + "hashes": [ + "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068", + "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768" + ], + "markers": "python_version >= '3.8'", + "version": "==4.2.0" + }, + "pluggy": { + "hashes": [ + "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981", + "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be" + ], + "markers": "python_version >= '3.8'", + "version": "==1.4.0" + }, + "pre-commit": { + "hashes": [ + "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32", + "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==3.5.0" + }, + "pytest": { + "hashes": [ + "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7", + "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==8.1.1" + }, + "pyyaml": { + "hashes": [ + "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5", + "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", + "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", + "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", + "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", + "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", + "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595", + "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", + "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", + "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", + "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", + "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", + "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", + "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6", + "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", + "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", + "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", + "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6", + "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", + "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", + "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", + "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", + "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", + "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", + "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", + "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", + "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", + "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", + "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", + "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef", + "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", + "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", + "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", + "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0", + "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", + "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c", + "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c", + "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", + "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", + "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", + "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", + "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", + "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", + "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", + "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", + "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", + "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", + "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", + "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585", + "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", + "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" + ], + "markers": "python_version >= '3.6'", + "version": "==6.0.1" + }, + "setuptools": { + "hashes": [ + "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e", + "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c" + ], + "markers": "python_version >= '3.8'", + "version": "==69.2.0" + }, + "virtualenv": { + "hashes": [ + "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a", + "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197" + ], + "markers": "python_version >= '3.7'", + "version": "==20.25.1" + } + } +} diff --git a/README.md b/README.md index 126c350..58ad28b 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,9 @@ -Flask-SimpleLDAP [![Build Status](https://app.travis-ci.com/alexferl/flask-simpleldap.svg?branch=master)](https://app.travis-ci.com/alexferl/flask-simpleldap) -================ - +# Flask-SimpleLDAP Flask-SimpleLDAP provides LDAP authentication for Flask. -Flask-SimpleLDAP is compatible with and tested on Python 3.7+. - -Quickstart ----------- +Flask-SimpleLDAP is compatible with and tested on Python 3.8+. +## Quickstart First, install Flask-SimpleLDAP: ```shell @@ -16,10 +12,10 @@ pip install flask-simpleldap Flask-SimpleLDAP depends, and will install for you, recent versions of Flask -(0.12.4 or later) and [python-ldap](https://python-ldap.org/). +(2.2.5 or later) and [python-ldap](https://python-ldap.org/). Please consult the [python-ldap installation instructions](https://www.python-ldap.org/en/latest/installing.html) if you get an error during installation. -Next, add an ``LDAP`` instance to your code and at least the three +Next, add an `LDAP` instance to your code and at least the three required configuration options. The complete sample from [examples/basic_auth/app.py](examples/basic_auth/app.py) looks like this: @@ -52,18 +48,18 @@ of the LDAP user, e.g. `me@mydomain.com`. Once you get the basic example working, check out the more complex ones: -* [examples/groups](examples/groups) demostrates using: - * `@ldap.login_required` for form/cookie-based auth, instead of basic HTTP authentication. - * `@ldap.group_required()` to restrict access to pages based on the user's LDAP groups. -* [examples/blueprints](examples/blueprints) implements the same functionality, but uses Flask's -[application factories](http://flask.pocoo.org/docs/patterns/appfactories/) -and [blueprints](http://flask.pocoo.org/docs/blueprints/). +- [examples/groups](examples/groups) demonstrates using: + - `@ldap.login_required` for form/cookie-based auth, instead of basic HTTP authentication. + - `@ldap.group_required()` to restrict access to pages based on the user's LDAP groups. +- [examples/blueprints](examples/blueprints) implements the same functionality, but uses Flask's +[application factories](https://flask.palletsprojects.com/en/3.0.x/patterns/appfactories/) +and [blueprints](https://flask.palletsprojects.com/en/3.0.x/blueprints/). OpenLDAP -------- -Add the ``LDAP`` instance to your code and depending on your OpenLDAP +Add the `LDAP` instance to your code and depending on your OpenLDAP configuration, add the following at least LDAP_USER_OBJECT_FILTER and LDAP_USER_OBJECT_FILTER. @@ -102,8 +98,6 @@ if __name__ == "__main__": app.run() ``` -Resources ---------- +## Resources -- [Documentation](http://flask-simpleldap.readthedocs.org/en/latest/) - [PyPI](https://pypi.python.org/pypi/Flask-SimpleLDAP) diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..227cea2 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +2.0.0 diff --git a/dev_requirements.txt b/dev_requirements.txt deleted file mode 100644 index 4c1a726..0000000 --- a/dev_requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -black==24.3.0 -pre-commit==2.17.0 -python-ldap==3.4.0 # here instead of requirements.txt so rtfd can build -Sphinx==2.1.2 - --r requirements.txt diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index 372cedc..0000000 --- a/docs/Makefile +++ /dev/null @@ -1,177 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build - -# User-friendly check for sphinx-build -ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) -endif - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " xml to make Docutils-native XML files" - @echo " pseudoxml to make pseudoxml-XML files for display purposes" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Flask-SimpleLDAP.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Flask-SimpleLDAP.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/Flask-SimpleLDAP" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Flask-SimpleLDAP" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -latexpdfja: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through platex and dvipdfmx..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." - -xml: - $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml - @echo - @echo "Build finished. The XML files are in $(BUILDDIR)/xml." - -pseudoxml: - $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml - @echo - @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs/_themes/LICENSE b/docs/_themes/LICENSE deleted file mode 100644 index 8daab7e..0000000 --- a/docs/_themes/LICENSE +++ /dev/null @@ -1,37 +0,0 @@ -Copyright (c) 2010 by Armin Ronacher. - -Some rights reserved. - -Redistribution and use in source and binary forms of the theme, with or -without modification, are permitted provided that the following conditions -are met: - -* Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - -* The names of the contributors may not be used to endorse or - promote products derived from this software without specific - prior written permission. - -We kindly ask you to only use these themes in an unmodified manner just -for Flask and Flask-related products, not for unrelated projects. If you -like the visual style and want to use it for your own projects, please -consider making some larger changes to the themes (such as changing -font faces, sizes, colors or margins). - -THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. diff --git a/docs/_themes/README b/docs/_themes/README deleted file mode 100644 index b3292bd..0000000 --- a/docs/_themes/README +++ /dev/null @@ -1,31 +0,0 @@ -Flask Sphinx Styles -=================== - -This repository contains sphinx styles for Flask and Flask related -projects. To use this style in your Sphinx documentation, follow -this guide: - -1. put this folder as _themes into your docs folder. Alternatively - you can also use git submodules to check out the contents there. -2. add this to your conf.py: - - sys.path.append(os.path.abspath('_themes')) - html_theme_path = ['_themes'] - html_theme = 'flask' - -The following themes exist: - -- 'flask' - the standard flask documentation theme for large - projects -- 'flask_small' - small one-page theme. Intended to be used by - very small addon libraries for flask. - -The following options exist for the flask_small theme: - - [options] - index_logo = '' filename of a picture in _static - to be used as replacement for the - h1 in the index.rst file. - index_logo_height = 120px height of the index logo - github_fork = '' repository name on github for the - "fork me" badge diff --git a/docs/_themes/flask/layout.html b/docs/_themes/flask/layout.html deleted file mode 100644 index 19c43fb..0000000 --- a/docs/_themes/flask/layout.html +++ /dev/null @@ -1,24 +0,0 @@ -{%- extends "basic/layout.html" %} -{%- block extrahead %} - {{ super() }} - {% if theme_touch_icon %} - - {% endif %} - -{% endblock %} -{%- block relbar2 %}{% endblock %} -{% block header %} - {{ super() }} - {% if pagename == 'index' %} -
- {% endif %} -{% endblock %} -{%- block footer %} - - {% if pagename == 'index' %} -
- {% endif %} -{%- endblock %} diff --git a/docs/_themes/flask/relations.html b/docs/_themes/flask/relations.html deleted file mode 100644 index 3bbcde8..0000000 --- a/docs/_themes/flask/relations.html +++ /dev/null @@ -1,19 +0,0 @@ -

Related Topics

- diff --git a/docs/_themes/flask/static/flasky.css_t b/docs/_themes/flask/static/flasky.css_t deleted file mode 100644 index 5906e75..0000000 --- a/docs/_themes/flask/static/flasky.css_t +++ /dev/null @@ -1,577 +0,0 @@ -/* - * flasky.css_t - * ~~~~~~~~~~~~ - * - * :copyright: Copyright 2010 by Armin Ronacher. - * :license: Flask Design License, see LICENSE for details. - */ - -{% set page_width = '940px' %} -{% set sidebar_width = '220px' %} - -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -body { - font-family: 'Georgia', serif; - font-size: 17px; - background-color: white; - color: #000; - margin: 0; - padding: 0; -} - -div.document { - width: {{ page_width }}; - margin: 30px auto 0 auto; -} - -div.documentwrapper { - float: left; - width: 100%; -} - -div.bodywrapper { - margin: 0 0 0 {{ sidebar_width }}; -} - -div.sphinxsidebar { - width: {{ sidebar_width }}; -} - -hr { - border: 1px solid #B1B4B6; -} - -div.body { - background-color: #ffffff; - color: #3E4349; - padding: 0 30px 0 30px; -} - -img.floatingflask { - padding: 0 0 10px 10px; - float: right; -} - -div.footer { - width: {{ page_width }}; - margin: 20px auto 30px auto; - font-size: 14px; - color: #888; - text-align: right; -} - -div.footer a { - color: #888; -} - -div.related { - display: none; -} - -div.sphinxsidebar a { - color: #444; - text-decoration: none; - border-bottom: 1px dotted #999; -} - -div.sphinxsidebar a:hover { - border-bottom: 1px solid #999; -} - -div.sphinxsidebar { - font-size: 14px; - line-height: 1.5; -} - -div.sphinxsidebarwrapper { - padding: 18px 10px; -} - -div.sphinxsidebarwrapper p.logo { - padding: 0 0 20px 0; - margin: 0; - text-align: center; -} - -div.sphinxsidebar h3, -div.sphinxsidebar h4 { - font-family: 'Garamond', 'Georgia', serif; - color: #444; - font-size: 24px; - font-weight: normal; - margin: 0 0 5px 0; - padding: 0; -} - -div.sphinxsidebar h4 { - font-size: 20px; -} - -div.sphinxsidebar h3 a { - color: #444; -} - -div.sphinxsidebar p.logo a, -div.sphinxsidebar h3 a, -div.sphinxsidebar p.logo a:hover, -div.sphinxsidebar h3 a:hover { - border: none; -} - -div.sphinxsidebar p { - color: #555; - margin: 10px 0; -} - -div.sphinxsidebar ul { - margin: 10px 0; - padding: 0; - color: #000; -} - -div.sphinxsidebar input { - border: 1px solid #ccc; - font-family: 'Georgia', serif; - font-size: 1em; -} - -/* -- body styles ----------------------------------------------------------- */ - -a { - color: #004B6B; - text-decoration: underline; -} - -a:hover { - color: #6D4100; - text-decoration: underline; -} - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: 'Garamond', 'Georgia', serif; - font-weight: normal; - margin: 30px 0px 10px 0px; - padding: 0; -} - -{% if theme_index_logo %} -div.indexwrapper h1 { - text-indent: -999999px; - background: url({{ theme_index_logo }}) no-repeat center center; - height: {{ theme_index_logo_height }}; -} -{% endif %} -div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } -div.body h2 { font-size: 180%; } -div.body h3 { font-size: 150%; } -div.body h4 { font-size: 130%; } -div.body h5 { font-size: 100%; } -div.body h6 { font-size: 100%; } - -a.headerlink { - color: #ddd; - padding: 0 4px; - text-decoration: none; -} - -a.headerlink:hover { - color: #444; - background: #eaeaea; -} - -div.body p, div.body dd, div.body li { - line-height: 1.4em; -} - -div.admonition { - background: #fafafa; - margin: 20px -30px; - padding: 10px 30px; - border-top: 1px solid #ccc; - border-bottom: 1px solid #ccc; -} - -div.admonition tt.xref, div.admonition a tt { - border-bottom: 1px solid #fafafa; -} - -dd div.admonition { - margin-left: -60px; - padding-left: 60px; -} - -div.admonition p.admonition-title { - font-family: 'Garamond', 'Georgia', serif; - font-weight: normal; - font-size: 24px; - margin: 0 0 10px 0; - padding: 0; - line-height: 1; -} - -div.admonition p.last { - margin-bottom: 0; -} - -div.highlight { - background-color: white; -} - -dt:target, .highlight { - background: #FAF3E8; -} - -div.note { - background-color: #eee; - border: 1px solid #ccc; -} - -div.seealso { - background-color: #ffc; - border: 1px solid #ff6; -} - -div.topic { - background-color: #eee; -} - -p.admonition-title { - display: inline; -} - -p.admonition-title:after { - content: ":"; -} - -pre, tt { - font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; - font-size: 0.9em; -} - -img.screenshot { -} - -tt.descname, tt.descclassname { - font-size: 0.95em; -} - -tt.descname { - padding-right: 0.08em; -} - -img.screenshot { - -moz-box-shadow: 2px 2px 4px #eee; - -webkit-box-shadow: 2px 2px 4px #eee; - box-shadow: 2px 2px 4px #eee; -} - -table.docutils { - border: 1px solid #888; - -moz-box-shadow: 2px 2px 4px #eee; - -webkit-box-shadow: 2px 2px 4px #eee; - box-shadow: 2px 2px 4px #eee; -} - -table.docutils td, table.docutils th { - border: 1px solid #888; - padding: 0.25em 0.7em; -} - -table.field-list, table.footnote { - border: none; - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; -} - -table.footnote { - margin: 15px 0; - width: 100%; - border: 1px solid #eee; - background: #fdfdfd; - font-size: 0.9em; -} - -table.footnote + table.footnote { - margin-top: -15px; - border-top: none; -} - -table.field-list th { - padding: 0 0.8em 0 0; -} - -table.field-list td { - padding: 0; -} - -table.footnote td.label { - width: 0px; - padding: 0.3em 0 0.3em 0.5em; -} - -table.footnote td { - padding: 0.3em 0.5em; -} - -dl { - margin: 0; - padding: 0; -} - -dl dd { - margin-left: 30px; -} - -blockquote { - margin: 0 0 0 30px; - padding: 0; -} - -ul, ol { - margin: 10px 0 10px 30px; - padding: 0; -} - -pre { - background: #eee; - padding: 7px 30px; - margin: 15px -30px; - line-height: 1.3em; -} - -dl pre, blockquote pre, li pre { - margin-left: -60px; - padding-left: 60px; -} - -dl dl pre { - margin-left: -90px; - padding-left: 90px; -} - -tt { - background-color: #ecf0f3; - color: #222; - /* padding: 1px 2px; */ -} - -tt.xref, a tt { - background-color: #FBFBFB; - border-bottom: 1px solid white; -} - -a.reference { - text-decoration: none; - border-bottom: 1px dotted #004B6B; -} - -a.reference:hover { - border-bottom: 1px solid #6D4100; -} - -a.footnote-reference { - text-decoration: none; - font-size: 0.7em; - vertical-align: top; - border-bottom: 1px dotted #004B6B; -} - -a.footnote-reference:hover { - border-bottom: 1px solid #6D4100; -} - -a:hover tt { - background: #EEE; -} - - -@media screen and (max-width: 870px) { - - div.sphinxsidebar { - display: none; - } - - div.document { - width: 100%; - - } - - div.documentwrapper { - margin-left: 0; - margin-top: 0; - margin-right: 0; - margin-bottom: 0; - } - - div.bodywrapper { - margin-top: 0; - margin-right: 0; - margin-bottom: 0; - margin-left: 0; - } - - ul { - margin-left: 0; - } - - .document { - width: auto; - } - - .footer { - width: auto; - } - - .bodywrapper { - margin: 0; - } - - .footer { - width: auto; - } - - .github { - display: none; - } - - - -} - - - -@media screen and (max-width: 875px) { - - body { - margin: 0; - padding: 20px 30px; - } - - div.documentwrapper { - float: none; - background: white; - } - - div.sphinxsidebar { - display: block; - float: none; - width: 102.5%; - margin: 50px -30px -20px -30px; - padding: 10px 20px; - background: #333; - color: white; - } - - div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, - div.sphinxsidebar h3 a { - color: white; - } - - div.sphinxsidebar a { - color: #aaa; - } - - div.sphinxsidebar p.logo { - display: none; - } - - div.document { - width: 100%; - margin: 0; - } - - div.related { - display: block; - margin: 0; - padding: 10px 0 20px 0; - } - - div.related ul, - div.related ul li { - margin: 0; - padding: 0; - } - - div.footer { - display: none; - } - - div.bodywrapper { - margin: 0; - } - - div.body { - min-height: 0; - padding: 0; - } - - .rtd_doc_footer { - display: none; - } - - .document { - width: auto; - } - - .footer { - width: auto; - } - - .footer { - width: auto; - } - - .github { - display: none; - } -} - - -/* scrollbars */ - -::-webkit-scrollbar { - width: 6px; - height: 6px; -} - -::-webkit-scrollbar-button:start:decrement, -::-webkit-scrollbar-button:end:increment { - display: block; - height: 10px; -} - -::-webkit-scrollbar-button:vertical:increment { - background-color: #fff; -} - -::-webkit-scrollbar-track-piece { - background-color: #eee; - -webkit-border-radius: 3px; -} - -::-webkit-scrollbar-thumb:vertical { - height: 50px; - background-color: #ccc; - -webkit-border-radius: 3px; -} - -::-webkit-scrollbar-thumb:horizontal { - width: 50px; - background-color: #ccc; - -webkit-border-radius: 3px; -} - -/* misc. */ - -.revsys-inline { - display: none!important; -} \ No newline at end of file diff --git a/docs/_themes/flask/theme.conf b/docs/_themes/flask/theme.conf deleted file mode 100644 index 18c720f..0000000 --- a/docs/_themes/flask/theme.conf +++ /dev/null @@ -1,9 +0,0 @@ -[theme] -inherit = basic -stylesheet = flasky.css -pygments_style = flask_theme_support.FlaskyStyle - -[options] -index_logo = '' -index_logo_height = 120px -touch_icon = diff --git a/docs/_themes/flask_small/layout.html b/docs/_themes/flask_small/layout.html deleted file mode 100644 index aa1716a..0000000 --- a/docs/_themes/flask_small/layout.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends "basic/layout.html" %} -{% block header %} - {{ super() }} - {% if pagename == 'index' %} -
- {% endif %} -{% endblock %} -{% block footer %} - {% if pagename == 'index' %} -
- {% endif %} -{% endblock %} -{# do not display relbars #} -{% block relbar1 %}{% endblock %} -{% block relbar2 %} - {% if theme_github_fork %} - Fork me on GitHub - {% endif %} -{% endblock %} -{% block sidebar1 %}{% endblock %} -{% block sidebar2 %}{% endblock %} diff --git a/docs/_themes/flask_small/static/flasky.css_t b/docs/_themes/flask_small/static/flasky.css_t deleted file mode 100644 index fe2141c..0000000 --- a/docs/_themes/flask_small/static/flasky.css_t +++ /dev/null @@ -1,287 +0,0 @@ -/* - * flasky.css_t - * ~~~~~~~~~~~~ - * - * Sphinx stylesheet -- flasky theme based on nature theme. - * - * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -body { - font-family: 'Georgia', serif; - font-size: 17px; - color: #000; - background: white; - margin: 0; - padding: 0; -} - -div.documentwrapper { - float: left; - width: 100%; -} - -div.bodywrapper { - margin: 40px auto 0 auto; - width: 700px; -} - -hr { - border: 1px solid #B1B4B6; -} - -div.body { - background-color: #ffffff; - color: #3E4349; - padding: 0 30px 30px 30px; -} - -img.floatingflask { - padding: 0 0 10px 10px; - float: right; -} - -div.footer { - text-align: right; - color: #888; - padding: 10px; - font-size: 14px; - width: 650px; - margin: 0 auto 40px auto; -} - -div.footer a { - color: #888; - text-decoration: underline; -} - -div.related { - line-height: 32px; - color: #888; -} - -div.related ul { - padding: 0 0 0 10px; -} - -div.related a { - color: #444; -} - -/* -- body styles ----------------------------------------------------------- */ - -a { - color: #004B6B; - text-decoration: underline; -} - -a:hover { - color: #6D4100; - text-decoration: underline; -} - -div.body { - padding-bottom: 40px; /* saved for footer */ -} - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: 'Garamond', 'Georgia', serif; - font-weight: normal; - margin: 30px 0px 10px 0px; - padding: 0; -} - -{% if theme_index_logo %} -div.indexwrapper h1 { - text-indent: -999999px; - background: url({{ theme_index_logo }}) no-repeat center center; - height: {{ theme_index_logo_height }}; -} -{% endif %} - -div.body h2 { font-size: 180%; } -div.body h3 { font-size: 150%; } -div.body h4 { font-size: 130%; } -div.body h5 { font-size: 100%; } -div.body h6 { font-size: 100%; } - -a.headerlink { - color: white; - padding: 0 4px; - text-decoration: none; -} - -a.headerlink:hover { - color: #444; - background: #eaeaea; -} - -div.body p, div.body dd, div.body li { - line-height: 1.4em; -} - -div.admonition { - background: #fafafa; - margin: 20px -30px; - padding: 10px 30px; - border-top: 1px solid #ccc; - border-bottom: 1px solid #ccc; -} - -div.admonition p.admonition-title { - font-family: 'Garamond', 'Georgia', serif; - font-weight: normal; - font-size: 24px; - margin: 0 0 10px 0; - padding: 0; - line-height: 1; -} - -div.admonition p.last { - margin-bottom: 0; -} - -div.highlight{ - background-color: white; -} - -dt:target, .highlight { - background: #FAF3E8; -} - -div.note { - background-color: #eee; - border: 1px solid #ccc; -} - -div.seealso { - background-color: #ffc; - border: 1px solid #ff6; -} - -div.topic { - background-color: #eee; -} - -div.warning { - background-color: #ffe4e4; - border: 1px solid #f66; -} - -p.admonition-title { - display: inline; -} - -p.admonition-title:after { - content: ":"; -} - -pre, tt { - font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; - font-size: 0.85em; -} - -img.screenshot { -} - -tt.descname, tt.descclassname { - font-size: 0.95em; -} - -tt.descname { - padding-right: 0.08em; -} - -img.screenshot { - -moz-box-shadow: 2px 2px 4px #eee; - -webkit-box-shadow: 2px 2px 4px #eee; - box-shadow: 2px 2px 4px #eee; -} - -table.docutils { - border: 1px solid #888; - -moz-box-shadow: 2px 2px 4px #eee; - -webkit-box-shadow: 2px 2px 4px #eee; - box-shadow: 2px 2px 4px #eee; -} - -table.docutils td, table.docutils th { - border: 1px solid #888; - padding: 0.25em 0.7em; -} - -table.field-list, table.footnote { - border: none; - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; -} - -table.footnote { - margin: 15px 0; - width: 100%; - border: 1px solid #eee; -} - -table.field-list th { - padding: 0 0.8em 0 0; -} - -table.field-list td { - padding: 0; -} - -table.footnote td { - padding: 0.5em; -} - -dl { - margin: 0; - padding: 0; -} - -dl dd { - margin-left: 30px; -} - -pre { - padding: 0; - margin: 15px -30px; - padding: 8px; - line-height: 1.3em; - padding: 7px 30px; - background: #eee; - border-radius: 2px; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; -} - -dl pre { - margin-left: -60px; - padding-left: 60px; -} - -tt { - background-color: #ecf0f3; - color: #222; - /* padding: 1px 2px; */ -} - -tt.xref, a tt { - background-color: #FBFBFB; -} - -a:hover tt { - background: #EEE; -} diff --git a/docs/_themes/flask_small/theme.conf b/docs/_themes/flask_small/theme.conf deleted file mode 100644 index 542b462..0000000 --- a/docs/_themes/flask_small/theme.conf +++ /dev/null @@ -1,10 +0,0 @@ -[theme] -inherit = basic -stylesheet = flasky.css -nosidebar = true -pygments_style = flask_theme_support.FlaskyStyle - -[options] -index_logo = '' -index_logo_height = 120px -github_fork = '' diff --git a/docs/_themes/flask_theme_support.py b/docs/_themes/flask_theme_support.py deleted file mode 100644 index 0dcf53b..0000000 --- a/docs/_themes/flask_theme_support.py +++ /dev/null @@ -1,89 +0,0 @@ -# flasky extensions. flasky pygments style based on tango style -from pygments.style import Style -from pygments.token import ( - Keyword, - Name, - Comment, - String, - Error, - Number, - Operator, - Generic, - Whitespace, - Punctuation, - Other, - Literal, -) - - -class FlaskyStyle(Style): - background_color = "#f8f8f8" - default_style = "" - - styles = { - # No corresponding class for the following: - # Text: "", # class: '' - Whitespace: "underline #f8f8f8", # class: 'w' - Error: "#a40000 border:#ef2929", # class: 'err' - Other: "#000000", # class 'x' - Comment: "italic #8f5902", # class: 'c' - Comment.Preproc: "noitalic", # class: 'cp' - Keyword: "bold #004461", # class: 'k' - Keyword.Constant: "bold #004461", # class: 'kc' - Keyword.Declaration: "bold #004461", # class: 'kd' - Keyword.Namespace: "bold #004461", # class: 'kn' - Keyword.Pseudo: "bold #004461", # class: 'kp' - Keyword.Reserved: "bold #004461", # class: 'kr' - Keyword.Type: "bold #004461", # class: 'kt' - Operator: "#582800", # class: 'o' - Operator.Word: "bold #004461", # class: 'ow' - like keywords - Punctuation: "bold #000000", # class: 'p' - # because special names such as Name.Class, Name.Function, etc. - # are not recognized as such later in the parsing, we choose them - # to look the same as ordinary variables. - Name: "#000000", # class: 'n' - Name.Attribute: "#c4a000", # class: 'na' - to be revised - Name.Builtin: "#004461", # class: 'nb' - Name.Builtin.Pseudo: "#3465a4", # class: 'bp' - Name.Class: "#000000", # class: 'nc' - to be revised - Name.Constant: "#000000", # class: 'no' - to be revised - Name.Decorator: "#888", # class: 'nd' - to be revised - Name.Entity: "#ce5c00", # class: 'ni' - Name.Exception: "bold #cc0000", # class: 'ne' - Name.Function: "#000000", # class: 'nf' - Name.Property: "#000000", # class: 'py' - Name.Label: "#f57900", # class: 'nl' - Name.Namespace: "#000000", # class: 'nn' - to be revised - Name.Other: "#000000", # class: 'nx' - Name.Tag: "bold #004461", # class: 'nt' - like a keyword - Name.Variable: "#000000", # class: 'nv' - to be revised - Name.Variable.Class: "#000000", # class: 'vc' - to be revised - Name.Variable.Global: "#000000", # class: 'vg' - to be revised - Name.Variable.Instance: "#000000", # class: 'vi' - to be revised - Number: "#990000", # class: 'm' - Literal: "#000000", # class: 'l' - Literal.Date: "#000000", # class: 'ld' - String: "#4e9a06", # class: 's' - String.Backtick: "#4e9a06", # class: 'sb' - String.Char: "#4e9a06", # class: 'sc' - String.Doc: "italic #8f5902", # class: 'sd' - like a comment - String.Double: "#4e9a06", # class: 's2' - String.Escape: "#4e9a06", # class: 'se' - String.Heredoc: "#4e9a06", # class: 'sh' - String.Interpol: "#4e9a06", # class: 'si' - String.Other: "#4e9a06", # class: 'sx' - String.Regex: "#4e9a06", # class: 'sr' - String.Single: "#4e9a06", # class: 's1' - String.Symbol: "#4e9a06", # class: 'ss' - Generic: "#000000", # class: 'g' - Generic.Deleted: "#a40000", # class: 'gd' - Generic.Emph: "italic #000000", # class: 'ge' - Generic.Error: "#ef2929", # class: 'gr' - Generic.Heading: "bold #000080", # class: 'gh' - Generic.Inserted: "#00A000", # class: 'gi' - Generic.Output: "#888", # class: 'go' - Generic.Prompt: "#745334", # class: 'gp' - Generic.Strong: "bold #000000", # class: 'gs' - Generic.Subheading: "bold #800080", # class: 'gu' - Generic.Traceback: "bold #a40000", # class: 'gt' - } diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index 47beca9..0000000 --- a/docs/conf.py +++ /dev/null @@ -1,286 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Flask-SimpleLDAP documentation build configuration file, created by -# sphinx-quickstart on Sun Aug 10 00:23:02 2014. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys -import os -from mock import Mock as MagicMock - - -class Mock(MagicMock): - @classmethod - def __getattr__(cls, name): - return Mock() - - -MOCK_MODULES = ["ldap"] -sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES) - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath("..")) - -# -- General configuration ----------- - -# If your documentation needs a minimal Sphinx version, state it here. -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - "sphinx.ext.autodoc", - "sphinx.ext.intersphinx", -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ["_templates"] - -# The suffix of source filenames. -source_suffix = ".rst" - -# The encoding of source files. -# source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = "index" - -# General information about the project. -project = "Flask-SimpleLDAP" -copyright = "2022, Alexandre Ferland" - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = "1.4.0" -# The full version, including alpha/beta/rc tags. -release = "1.4.0" - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -# today = '' -# Else, today_fmt is used as the format for a strftime call. -# today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ["_build"] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -# default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -# add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -# add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -# show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = "sphinx" - -# A list of ignored prefixes for module index sorting. -# modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -# keep_warnings = False - - -# -- Options for HTML output ---------------------------------------------- - -sys.path.append(os.path.abspath("_themes")) -html_theme_path = ["_themes"] -html_theme = "flask_small" -html_theme_options = { - "index_logo": "", # TODO - "github_fork": "alexferl/flask-simpleldap", -} - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -# html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -# html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -# html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -# html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["_static"] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -# html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -# html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -# html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -# html_additional_pages = {} - -# If false, no module index is generated. -# html_domain_indices = True - -# If false, no index is generated. -# html_use_index = True - -# If true, the index is split into individual pages for each letter. -# html_split_index = False - -# If true, links to the reST sources are added to the pages. -# html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -# html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -# html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -# html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -# html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = "Flask-SimpleLDAPdoc" - - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - #'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). - #'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. - #'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - ( - "index", - "Flask-SimpleLDAP.tex", - "Flask-SimpleLDAP Documentation", - "Alexandre Ferland", - "manual", - ), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -# latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -# latex_use_parts = False - -# If true, show page references after internal links. -# latex_show_pagerefs = False - -# If true, show URL addresses after external links. -# latex_show_urls = False - -# Documents to append as an appendix to all manuals. -# latex_appendices = [] - -# If false, no module index is generated. -# latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ( - "index", - "flask-simpleldap", - "Flask-SimpleLDAP Documentation", - ["Alexandre Ferland"], - 1, - ) -] - -# If true, show URL addresses after external links. -# man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ( - "index", - "Flask-SimpleLDAP", - "Flask-SimpleLDAP Documentation", - "Alexandre Ferland", - "Flask-SimpleLDAP", - "One line description of project.", - "Miscellaneous", - ), -] - -# Documents to append as an appendix to all manuals. -# texinfo_appendices = [] - -# If false, no module index is generated. -# texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -# texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -# texinfo_no_detailmenu = False - - -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {"http://docs.python.org/": None} diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index e4bb3ac..0000000 --- a/docs/index.rst +++ /dev/null @@ -1,206 +0,0 @@ -.. Flask-SimpleLDAP documentation master file, created by - sphinx-quickstart on Sat Aug 9 19:44:30 2014. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to Flask-SimpleLDAP's documentation! -============================================ - -Flask-SimpleLDAP provides LDAP authentication for Flask. - - -Quickstart ----------- - -First, install Flask-SimpleLDAP: - - .. code-block:: bash - - pip install flask-simpleldap - -Flask-SimpleLDAP depends, and will install for you, recent versions of Flask -(0.12.4 or later) and pyldap. Flask-SimpleLDAP is compatible -with and tested on Python 3.7+. - -Next, add a :class:`~flask_simpleldap.LDAP` to your code and at least the three -required configuration options: - - .. code-block:: python - - from flask import Flask - from flask_simpleldap import LDAP - - app = Flask(__name__) - # app.config["LDAP_HOST"] = "ldap.example.org" # defaults to localhost - app.config["LDAP_BASE_DN"] = "OU=users,dc=example,dc=org" - app.config["LDAP_USERNAME"] = "CN=user,OU=Users,DC=example,DC=org" - app.config["LDAP_PASSWORD"] = "password" - - ldap = LDAP(app) - - @app.route("/ldap") - @ldap.login_required - def ldap_protected(): - return "Success!" - - - if __name__ == "__main__": - app.run() - - -Configuration -------------- - -:class:`~flask_simpleldap.LDAP` understands the following configuration -directives: - -================================== ================================================================ -``LDAP_HOST`` The host name or IP address of your LDAP server. - Default: 'localhost'. -``LDAP_PORT`` The port number of your LDAP server. Default: 389. -``LDAP_SCHEMA`` The LDAP schema to use between 'ldap' and 'ldaps'. - Default: 'ldap'. -``LDAP_SOCKET_PATH`` If ``LDAP_SCHEMA`` is set to `ldapi`, the - path to the Unix socket path. Default: `/`. -``LDAP_USERNAME`` **Required**: The user name used to bind. -``LDAP_PASSWORD`` **Required**: The password used to bind. -``LDAP_TIMEOUT`` How long (seconds) a connection can take to be opened - before timing out. Default: 10. -``LDAP_USE_SSL`` Set to ``True`` if your server uses SSL. - Default: ``False``. -``LDAP_USE_TLS`` Set to ``True`` if your server uses TLS. - Default: ``False``. -``LDAP_REQUIRE_CERT`` Set to ``True`` if your server requires a certificate. - Default: ``False``. -``LDAP_CERT_PATH`` Path to the certificate if ``LDAP_REQUIRE_CERT`` is - ``True``. -``LDAP_BASE_DN`` **Required**: The distinguished name to use as the search base. -``LDAP_OBJECTS_DN`` The field to use as the objects' distinguished name. - Default: 'distinguishedName'. -``LDAP_USER_FIELDS`` ``list`` of fields to return when searching for a user's - object details. Default: ``list`` (all). -``LDAP_USER_OBJECT_FILTER`` The filter to use when searching for a user object. - Default: '(&(objectclass=Person)(userPrincipalName=%s))' -``LDAP_USERS_OBJECT_FILTER`` The filter to use when searching for users objects. - Default: 'objectclass=Person' -``LDAP_USER_GROUPS_FIELD`` The field to return when searching for a user's - groups. Default: 'memberOf'. -``LDAP_GROUPS_OBJECT_FILTER`` The filter to use when searching for groups objects. - Default: 'objectclass=Group' -``LDAP_GROUP_FIELDS`` ``list`` of fields to return when searching for a group's - object details. Default: ``list`` (all). -``LDAP_GROUP_OBJECT_FILTER`` The filter to use when searching for a group object. - Default: '(&(objectclass=Group)(userPrincipalName=%s))' -``LDAP_GROUP_MEMBERS_FIELD`` The field to return when searching for a group's members. - Default: 'member' -``LDAP_LOGIN_VIEW`` Views decorated with :meth:`.login_required()` or - :meth:`.group_required()` will redirect - unauthenticated requests to this view. Default: - 'login'. -``LDAP_REALM_NAME`` Views decorated with - :meth:`.basic_auth_required()` will use this as - the "realm" part of HTTP Basic Authentication when - responding to unauthenticated requests. -``LDAP_OPENLDAP`` Set to ``True`` if your server is running OpenLDAP. - Default: ``False`` -``LDAP_GROUP_MEMBER_FILTER`` The group member filter to use when using OpenLDAP. - Default: '*' -``LDAP_GROUP_MEMBER_FILTER_FIELD`` The group member filter field to use when using OpenLDAP. - Default: '*' -``LDAP_CUSTOM_OPTIONS`` ``dict`` of ldap options you want to set in this format: {option: value}. - Default: ``None`` -================================== ================================================================ - - -API -=== - -Classes -------- - -.. autoclass:: flask_simpleldap.LDAP - :members: - - -History -------- - -Changes: - -- 1.4.0 July 16, 2019 - - - This release drops support for `Python 2.7 `_. If you're still on Python 2.7, you can use `v1.3.3 `_. - - - Fixes: - - - `#62 `_ get_object_details returning None - -- 1.3.0 July 14, 2019 - - - Thanks to the contributors, this release fixes issues related to bind_user and fixes some issues related to filtering. - - - `#51 `_ Referral chasing crash - - - `#54 `_ Fixes #44 - Error in bind_user method, also fixes #60 and #61 - - - `#56 `_ OpenLDAP section has Incorrect LDAP_GROUP_OBJECT_FILTER - - - `#57 `_ next vaule: Priority use request.full_path - - - `#59 `_ get_object_details to take query_filter and fallback to LDAP_USER_OBJECT_FILTER or LDAP_GROUP_OBJECT_FILTER - - -- 1.2.0 September 26, 2017 - - - Changed get_group_members() and get_user_groups() returning strings instead of bytes in PY3. - -- 1.1.2 July 17, 2017 - - - Merge GitHub PR `#30 `_, - Fix for python3 - - Fix decoding bytes in PY3 for @ldap.group_required. - -- 1.1.1 April 10, 2017 - - - Merge GitHub pull `#26 `_, - Fix set_option call to LDAP for SSL CERT - -- 1.1.0 June 7, 2016 - - - Add the ability the pass any valid pyldap config options via the LDAP_CUSTOM_OPTIONS configuration directive. - -- 1.0.1 June 5, 2016 - - - Fix ldap filter import. - -- 1.0.0 June 4, 2016 - - - Python 3.x support. Switched from python-ldap to pyldap which is a fork with Python 3.x support. - -- 0.4.0: September 5, 2015 - - - Added support for OpenLDAP directories. Thanks to `@jm66 `_ on GitHub. - -- 0.3.0: January 21, 2015 - - - Fix Github issue `#10 `_, - Redirect users back to the page they originally requested after authenticating - - - Fix GitHub issue `#12 `_, - Only trust .bind_user() with a non-empty password - -- 0.2.0: December 7, 2014 - - - Added HTTP Basic Authentication. Thanks to `@OptiverTimAll `_ on GitHub. - - Fix GitHub issue `#4 `_, - User or group queries are vulnerable to LDAP injection. - Make sure you update your filters to use '%s' instead of the old '{}'! - -- 0.1.1: September 6, 2014 - - - Fix GitHub issue `#3 `_, - Not compatible with uppercase distinguished names. - -- 0.1: August 9, 2014 - - - Initial Release diff --git a/examples/basic_auth/app.py b/examples/basic_auth/app.py index 12bad8b..e214831 100644 --- a/examples/basic_auth/app.py +++ b/examples/basic_auth/app.py @@ -2,7 +2,7 @@ from flask_simpleldap import LDAP app = Flask(__name__) -#app.config["LDAP_HOST"] = "ldap.example.org" # defaults to localhost +# app.config["LDAP_HOST"] = "ldap.example.org" # defaults to localhost app.config["LDAP_BASE_DN"] = "OU=users,dc=example,dc=org" app.config["LDAP_USERNAME"] = "CN=user,OU=Users,DC=example,DC=org" app.config["LDAP_PASSWORD"] = "password" @@ -16,7 +16,7 @@ @app.route("/") @ldap.basic_auth_required def index(): - return "Welcome, {0}!".format(g.ldap_username) + return f"Welcome, {g.ldap_username}!" if __name__ == "__main__": diff --git a/examples/basic_auth/app_oldap.py b/examples/basic_auth/app_oldap.py index dce343b..21d056d 100644 --- a/examples/basic_auth/app_oldap.py +++ b/examples/basic_auth/app_oldap.py @@ -21,9 +21,9 @@ app.config["LDAP_GROUP_OBJECT_FILTER"] = "(&(objectclass=groupOfUniqueNames)(cn=%s))" app.config["LDAP_GROUPS_OBJECT_FILTER"] = "objectclass=groupOfUniqueNames" app.config["LDAP_GROUP_FIELDS"] = ["cn", "entryDN", "member", "description"] -app.config[ - "LDAP_GROUP_MEMBER_FILTER" -] = "(&(cn=*)(objectclass=groupOfUniqueNames)(member=%s))" +app.config["LDAP_GROUP_MEMBER_FILTER"] = ( + "(&(cn=*)(objectclass=groupOfUniqueNames)(member=%s))" +) app.config["LDAP_GROUP_MEMBER_FILTER_FIELD"] = "cn" ldap = LDAP(app) @@ -32,7 +32,7 @@ @app.route("/") @ldap.basic_auth_required def index(): - return "Welcome, {0}!".format(g.ldap_username) + return f"Welcome, {g.ldap_username}!" if __name__ == "__main__": diff --git a/examples/blueprints/blueprints/config.py b/examples/blueprints/blueprints/config.py index 8c0a5f3..6e8baa7 100644 --- a/examples/blueprints/blueprints/config.py +++ b/examples/blueprints/blueprints/config.py @@ -7,9 +7,9 @@ class BaseConfig(object): DEBUG = True # LDAP - LDAP_HOST = "ldap.example.org" - LDAP_BASE_DN = "OU=users,dc=example,dc=org" - LDAP_USERNAME = "CN=user,OU=Users,DC=example,DC=org" - LDAP_PASSWORD = "password" + # LDAP_HOST = "ldap.example.org" # defaults to localhost + LDAP_BASE_DN = "dc=example,dc=org" + LDAP_USERNAME = "cn=admin,dc=example,dc=org" + LDAP_PASSWORD = "admin" LDAP_LOGIN_VIEW = "core.login" LDAP_CUSTOM_OPTIONS = {ldap.OPT_REFERRALS: 0} diff --git a/examples/groups/app_oldap.py b/examples/groups/app_oldap.py index 4fd547a..2cb4001 100644 --- a/examples/groups/app_oldap.py +++ b/examples/groups/app_oldap.py @@ -19,9 +19,9 @@ app.config["LDAP_GROUP_OBJECT_FILTER"] = "(&(objectclass=groupOfUniqueNames)(cn=%s))" app.config["LDAP_GROUPS_OBJECT_FILTER"] = "objectclass=groupOfUniqueNames" app.config["LDAP_GROUP_FIELDS"] = ["cn", "entryDN", "member", "description"] -app.config[ - "LDAP_GROUP_MEMBER_FILTER" -] = "(&(cn=*)(objectclass=groupOfUniqueNames)(member=%s))" +app.config["LDAP_GROUP_MEMBER_FILTER"] = ( + "(&(cn=*)(objectclass=groupOfUniqueNames)(member=%s))" +) app.config["LDAP_GROUP_MEMBER_FILTER_FIELD"] = "cn" ldap = LDAP(app) diff --git a/flask_simpleldap/__init__.py b/flask_simpleldap/__init__.py index 6d73cfd..858f5c8 100644 --- a/flask_simpleldap/__init__.py +++ b/flask_simpleldap/__init__.py @@ -46,14 +46,15 @@ def init_app(app): app.config.setdefault("LDAP_OBJECTS_DN", "distinguishedName") app.config.setdefault("LDAP_USER_FIELDS", []) app.config.setdefault("LDAP_USER_GROUPS_FIELD", "memberOf") - app.config.setdefault("LDAP_USER_OBJECT_FILTER", - "(&(objectclass=Person)(userPrincipalName=%s))") - app.config.setdefault("LDAP_USERS_OBJECT_FILTER", - "objectclass=Person") + app.config.setdefault( + "LDAP_USER_OBJECT_FILTER", "(&(objectclass=Person)(userPrincipalName=%s))" + ) + app.config.setdefault("LDAP_USERS_OBJECT_FILTER", "objectclass=Person") app.config.setdefault("LDAP_GROUP_FIELDS", []) app.config.setdefault("LDAP_GROUP_MEMBERS_FIELD", "member") - app.config.setdefault("LDAP_GROUP_OBJECT_FILTER", - "(&(objectclass=Group)(userPrincipalName=%s))") + app.config.setdefault( + "LDAP_GROUP_OBJECT_FILTER", "(&(objectclass=Group)(userPrincipalName=%s))" + ) app.config.setdefault("LDAP_GROUPS_OBJECT_FILTER", "objectclass=Group") app.config.setdefault("LDAP_LOGIN_VIEW", "login") app.config.setdefault("LDAP_REALM_NAME", "LDAP authentication") @@ -63,22 +64,19 @@ def init_app(app): app.config.setdefault("LDAP_CUSTOM_OPTIONS", None) if app.config["LDAP_USE_SSL"] or app.config["LDAP_USE_TLS"]: - ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, - ldap.OPT_X_TLS_NEVER) + ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) if app.config["LDAP_REQUIRE_CERT"]: - ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, - ldap.OPT_X_TLS_DEMAND) - ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, - app.config["LDAP_CERT_PATH"]) + ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND) + ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, app.config["LDAP_CERT_PATH"]) if app.config["LDAP_BASE_DN"] is None: raise LDAPException("LDAP_BASE_DN cannot be None!") if app.config["LDAP_SCHEMA"] != "ldapi": for option in ["USERNAME", "PASSWORD"]: - if app.config["LDAP_{0}".format(option)] is None: - raise LDAPException("LDAP_{0} cannot be None!".format(option)) + if app.config[f"LDAP_{option}"] is None: + raise LDAPException(f"LDAP_{option} cannot be None!") @staticmethod def _set_custom_options(conn): @@ -97,16 +95,9 @@ def initialize(self): try: if current_app.config["LDAP_SCHEMA"] == "ldapi": - uri = "{0}://{1}".format( - current_app.config["LDAP_SCHEMA"], - current_app.config["LDAP_SOCKET_PATH"], - ) + uri = f"{current_app.config['LDAP_SCHEMA']}://{current_app.config['LDAP_SOCKET_PATH']}" else: - uri = "{0}://{1}:{2}".format( - current_app.config["LDAP_SCHEMA"], - current_app.config["LDAP_HOST"], - current_app.config["LDAP_PORT"], - ) + uri = f"{current_app.config['LDAP_SCHEMA']}://{current_app.config['LDAP_HOST']}:{current_app.config['LDAP_PORT']}" conn = ldap.initialize(uri) conn.set_option( ldap.OPT_NETWORK_TIMEOUT, current_app.config["LDAP_TIMEOUT"] @@ -189,14 +180,18 @@ def get_users(self, fields=None, dn_only=False): fields = fields or current_app.config["LDAP_USER_FIELDS"] if current_app.config["LDAP_OPENLDAP"]: records = conn.search_s( - current_app.config["LDAP_BASE_DN"], ldap.SCOPE_SUBTREE, + current_app.config["LDAP_BASE_DN"], + ldap.SCOPE_SUBTREE, current_app.config["LDAP_USERS_OBJECT_FILTER"], - fields) + fields, + ) else: records = conn.search_s( - current_app.config["LDAP_BASE_DN"], ldap.SCOPE_SUBTREE, + current_app.config["LDAP_BASE_DN"], + ldap.SCOPE_SUBTREE, current_app.config["LDAP_USERS_OBJECT_FILTER"], - fields) + fields, + ) conn.unbind_s() if records: if dn_only: @@ -208,8 +203,9 @@ def get_users(self, fields=None, dn_only=False): except ldap.LDAPError as e: raise LDAPException(self.error(e.args)) - def get_object_details(self, user=None, group=None, query_filter=None, - dn_only=False): + def get_object_details( + self, user=None, group=None, query_filter=None, dn_only=False + ): """Returns a ``dict`` with the object's (user or group) details. :param str user: Username of the user object you want details for. @@ -410,10 +406,10 @@ def wrapped(*args, **kwargs): if g.user is None: next_path = request.full_path or request.path if next_path == "/?": - return redirect( - url_for(current_app.config["LDAP_LOGIN_VIEW"])) - return redirect(url_for(current_app.config["LDAP_LOGIN_VIEW"], - next=next_path)) + return redirect(url_for(current_app.config["LDAP_LOGIN_VIEW"])) + return redirect( + url_for(current_app.config["LDAP_LOGIN_VIEW"], next=next_path) + ) return func(*args, **kwargs) return wrapped @@ -470,7 +466,9 @@ def basic_auth_required(self, func): def make_auth_required_response(): response = make_response("Unauthorized", 401) - response.headers['WWW-Authenticate'] = f'Basic realm="{current_app.config["LDAP_REALM_NAME"]}"' + response.headers["WWW-Authenticate"] = ( + f'Basic realm="{current_app.config["LDAP_REALM_NAME"]}"' + ) return response @wraps(func) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..fed528d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..fc11e9c --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,19 @@ +-i https://pypi.org/simple +black==24.3.0; python_version >= '3.8' +cfgv==3.4.0; python_version >= '3.8' +click==8.1.7; python_version >= '3.7' +distlib==0.3.8 +filelock==3.13.3; python_version >= '3.8' +identify==2.5.35; python_version >= '3.8' +iniconfig==2.0.0; python_version >= '3.7' +mypy-extensions==1.0.0; python_version >= '3.5' +nodeenv==1.8.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6' +packaging==24.0; python_version >= '3.7' +pathspec==0.12.1; python_version >= '3.8' +platformdirs==4.2.0; python_version >= '3.8' +pluggy==1.4.0; python_version >= '3.8' +pre-commit==3.5.0; python_version >= '3.8' +pytest==8.1.1; python_version >= '3.8' +pyyaml==6.0.1; python_version >= '3.6' +setuptools==69.2.0; python_version >= '3.8' +virtualenv==20.25.1; python_version >= '3.7' diff --git a/requirements.txt b/requirements.txt index a506103..8169b26 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,11 @@ -Flask==2.3.2 -mock==4.0.3 # for ci +-i https://pypi.org/simple +blinker==1.7.0; python_version >= '3.8' +click==8.1.7; python_version >= '3.7' +flask==3.0.2; python_version >= '3.8' +itsdangerous==2.1.2; python_version >= '3.7' +jinja2==3.1.3; python_version >= '3.7' +markupsafe==2.1.5; python_version >= '3.7' +pyasn1==0.6.0; python_version >= '3.8' +pyasn1-modules==0.4.0; python_version >= '3.8' +python-ldap==3.4.4; python_version >= '3.6' +werkzeug==3.0.2; python_version >= '3.8' diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..d7153e4 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,29 @@ +[metadata] +name = Flask-SimpleLDAP +version = file: VERSION +author = Alexandre Ferland +author_email = me@alexferl.com +url = https://github.com/alexferl/flask-simpleldap +description = LDAP authentication extension for Flask +long_description = file: README.md +long_description_content_type = text/markdown +license = MIT +classifiers = + Environment :: Web Environment + Intended Audience :: Developers + License :: OSI Approved :: MIT License + Operating System :: OS Independent + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 + Topic :: Software Development :: Libraries :: Python Modules + +[options] +zip_safe = False +include_package_data = True +python_requires = >=3.8 +install_requires = + Flask>=2.2.5 + python-ldap>=3.0.0 diff --git a/setup.py b/setup.py deleted file mode 100644 index 3897a38..0000000 --- a/setup.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Flask-SimpleLDAP ----------------- - -LDAP authentication extension for Flask -""" -from setuptools import setup - -from pathlib import Path - -this_directory = Path(__file__).parent -long_description = (this_directory / "README.md").read_text() - - -setup( - name="Flask-SimpleLDAP", - version="1.4.0", - url="https://github.com/alexferl/flask-simpleldap", - license="MIT", - author="Alexandre Ferland", - author_email="me@alexferl.com", - description="LDAP authentication extension for Flask", - long_description=long_description, - long_description_content_type="text/markdown", - packages=["flask_simpleldap"], - zip_safe=False, - include_package_data=True, - platforms="any", - install_requires=["Flask>=0.12.4", "python-ldap>=3.0.0"], - classifiers=[ - "Environment :: Web Environment", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Topic :: Software Development :: Libraries :: Python Modules", - ], -) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_simpleldap.py b/tests/test_simpleldap.py new file mode 100644 index 0000000..1e5a792 --- /dev/null +++ b/tests/test_simpleldap.py @@ -0,0 +1,32 @@ +import pytest +from flask import Flask +from flask_simpleldap import LDAP, LDAPException + + +def test_instantiate(): + app = Flask(__name__) + app.config["LDAP_BASE_DN"] = "OU=users,dc=example,dc=org" + app.config["LDAP_USERNAME"] = "CN=user,OU=Users,DC=example,DC=org" + app.config["LDAP_PASSWORD"] = "password" + LDAP(app) + + +def test_instantiate_no_dn(): + app = Flask(__name__) + with pytest.raises(LDAPException, match="LDAP_BASE_DN cannot be None!"): + LDAP(app) + + +def test_instantiate_no_username(): + app = Flask(__name__) + app.config["LDAP_BASE_DN"] = "OU=users,dc=example,dc=org" + with pytest.raises(LDAPException, match="LDAP_USERNAME cannot be None!"): + LDAP(app) + + +def test_instantiate_no_password(): + app = Flask(__name__) + app.config["LDAP_BASE_DN"] = "OU=users,dc=example,dc=org" + app.config["LDAP_USERNAME"] = "CN=user,OU=Users,DC=example,DC=org" + with pytest.raises(LDAPException, match="LDAP_PASSWORD cannot be None!"): + LDAP(app) From 8bf68208bd7f1ed16bae85b0fd0502cd033ae57d Mon Sep 17 00:00:00 2001 From: alexferl Date: Wed, 3 Apr 2024 14:32:46 -0400 Subject: [PATCH 2/5] add apt deps Signed-off-by: alexferl --- .github/workflows/test.yaml | 7 +++++++ README.md | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 98f942b..4028323 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -58,6 +58,13 @@ jobs: python-version: '3.12' FLASK: 2.2.5 steps: + - name: Install apt dependencies + run: | + set -ex + sudo apt update + sudo apt install -y ldap-utils slapd enchant-2 libldap2-dev libsasl2-dev apparmor-utils + - name: Disable AppArmor + run: sudo aa-disable /usr/sbin/slapd - name: 'Set up Python ${{ matrix.python-version }}' uses: actions/setup-python@v2 with: diff --git a/README.md b/README.md index 58ad28b..dd908ea 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ ldap = LDAP(app) @app.route("/") @ldap.basic_auth_required def index(): - return "Welcome, {0}!".format(g.ldap_username) + return f"Welcome, {g.ldap_username}!" if __name__ == "__main__": app.run() @@ -92,7 +92,7 @@ ldap = LDAP(app) @app.route("/") @ldap.basic_auth_required def index(): - return "Welcome, {0}!".format(g.ldap_username) + return f"Welcome, {g.ldap_username}!" if __name__ == "__main__": app.run() From 2dd0d99cee59b1d5a7d09535ed36503bf0baa172 Mon Sep 17 00:00:00 2001 From: alexferl Date: Wed, 3 Apr 2024 14:35:44 -0400 Subject: [PATCH 3/5] update version Signed-off-by: alexferl --- .github/workflows/test.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 4028323..3100a1d 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -66,10 +66,10 @@ jobs: - name: Disable AppArmor run: sudo aa-disable /usr/sbin/slapd - name: 'Set up Python ${{ matrix.python-version }}' - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: '${{ matrix.python-version }}' - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - run: pip install pytest Flask==$FLASK env: FLASK: '${{ matrix.FLASK }}' From 9699defaaabe053af5cfa6f95a9ad853e79d6561 Mon Sep 17 00:00:00 2001 From: alexferl Date: Wed, 3 Apr 2024 14:38:34 -0400 Subject: [PATCH 4/5] update setup-python version Signed-off-by: alexferl --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 3100a1d..800d02a 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -66,7 +66,7 @@ jobs: - name: Disable AppArmor run: sudo aa-disable /usr/sbin/slapd - name: 'Set up Python ${{ matrix.python-version }}' - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '${{ matrix.python-version }}' - uses: actions/checkout@v4 From 1a3b0d19309aeeb85d3418f0bd868c2688e69ad7 Mon Sep 17 00:00:00 2001 From: alexferl Date: Wed, 3 Apr 2024 15:07:32 -0400 Subject: [PATCH 5/5] add config docs Signed-off-by: alexferl --- README.md | 48 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index dd908ea..9e205b8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # Flask-SimpleLDAP -Flask-SimpleLDAP provides LDAP authentication for Flask. - -Flask-SimpleLDAP is compatible with and tested on Python 3.8+. +Flask-SimpleLDAP provides LDAP authentication for Flask and is compatible with and tested on Python 3.8+. ## Quickstart First, install Flask-SimpleLDAP: @@ -10,8 +8,7 @@ First, install Flask-SimpleLDAP: pip install flask-simpleldap ``` - -Flask-SimpleLDAP depends, and will install for you, recent versions of Flask +Flask-SimpleLDAP depends, and will install for you, a recent version of Flask (2.2.5 or later) and [python-ldap](https://python-ldap.org/). Please consult the [python-ldap installation instructions](https://www.python-ldap.org/en/latest/installing.html) if you get an error during installation. @@ -56,12 +53,10 @@ Once you get the basic example working, check out the more complex ones: and [blueprints](https://flask.palletsprojects.com/en/3.0.x/blueprints/). -OpenLDAP --------- - +## OpenLDAP Add the `LDAP` instance to your code and depending on your OpenLDAP -configuration, add the following at least LDAP_USER_OBJECT_FILTER and -LDAP_USER_OBJECT_FILTER. +configuration, add the following at least `LDAP_USER_OBJECT_FILTER` and +`LDAP_USER_OBJECT_FILTER`. ```python from flask import Flask, g @@ -98,6 +93,33 @@ if __name__ == "__main__": app.run() ``` -## Resources - -- [PyPI](https://pypi.python.org/pypi/Flask-SimpleLDAP) +## Configuration +| Setting | Description | +|----------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| +| `LDAP_HOST` | The host name or IP address of your LDAP server. Default: `"localhost"`. | +| `LDAP_PORT` | The port number of your LDAP server. Default: `389`. | +| `LDAP_SCHEMA` | The LDAP schema to use between `"ldap"`, `"ldapi"` and `"ldaps"`. Default: `"ldap"`. | +| `LDAP_SOCKET_PATH` | If `LDAP_SCHEMA` is set to `"ldapi"`, the path to the Unix socket path. Default: `"/"`. | +| `LDAP_USERNAME` | **Required**: The username used to bind. | +| `LDAP_PASSWORD` | **Required**: The password used to bind. | +| `LDAP_TIMEOUT` | How long (seconds) a connection can take to be opened before timing out. Default: `10`. | +| `LDAP_LOGIN_VIEW` | Views decorated with `.login_required()` or`.group_required()` will redirect unauthenticated requests to this view. Default: `"login"`. | +| `LDAP_REALM_NAME` | Views decorated with `.basic_auth_required()` will use this as the "realm" part of HTTP Basic Authentication when responding to unauthenticated requests. | +| `LDAP_OPENLDAP` | Set to `True` if your server is running OpenLDAP. Default: `False`. | +| `LDAP_USE_SSL` | Set to `True` if your server uses SSL. Default: `False`. | +| `LDAP_USE_TLS` | Set to `True` if your server uses TLS. Default: `False`. | +| `LDAP_REQUIRE_CERT` | Set to `True` if your server requires a certificate. Default: `False`. | +| `LDAP_CERT_PATH` | Path to the certificate if `LDAP_REQUIRE_CERT` is `True`. | +| `LDAP_CUSTOM_OPTIONS` | `dict` of ldap options you want to set in this format: `{option: value}`. Default: `None`. | +| `LDAP_BASE_DN` | **Required**: The distinguished name to use as the search base. | +| `LDAP_OBJECTS_DN` | The field to use as the objects' distinguished name. Default: `"distinguishedName"`. | +| `LDAP_USER_FIELDS` | `list` of fields to return when searching for a user's object details. Default: `[]` (all). | +| `LDAP_USER_GROUPS_FIELD` | The field to return when searching for a user's groups. Default: `"memberOf"`. | +| `LDAP_USER_OBJECT_FILTER` | The filter to use when searching for a user object. Default: `"(&(objectclass=Person)(userPrincipalName=%s))"` | +| `LDAP_USERS_OBJECT_FILTER` | The filter to use when searching for users objects. Default: `"objectclass=Person"` | +| `LDAP_GROUP_FIELDS` | `list` of fields to return when searching for a group's object details. Default: `[]` (all). | +| `LDAP_GROUP_MEMBER_FILTER` | The group member filter to use when using OpenLDAP. Default: `"*"`. | +| `LDAP_GROUP_MEMBER_FILTER_FIELD` | The group member filter field to use when using OpenLDAP. Default: `"*"`. | +| `LDAP_GROUP_MEMBERS_FIELD` | The field to return when searching for a group's members. Default: `"member"`. | +| `LDAP_GROUP_OBJECT_FILTER` | The filter to use when searching for a group object. Default: `"(&(objectclass=Group)(userPrincipalName=%s))"`. | +| `LDAP_GROUPS_OBJECT_FILTER` | The filter to use when searching for groups objects. Default: `"objectclass=Group"`. |