Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • ldapi-support
  • master
  • v0.1.0
  • v0.1.1
  • v0.2.0
  • v0.3.0
  • v0.4.0
  • v1.0.0
  • v1.0.1
  • v1.1.0
  • v1.1.1
  • v1.1.2
  • v1.3.0
  • v1.3.2
  • v1.3.3
  • v1.4.0
16 results

Target

Select target project
  • nimrod/flask-simpleldap
1 result
Select Git revision
  • ldapi-support
  • master
  • v0.1.0
  • v0.1.1
  • v0.2.0
  • v0.3.0
  • v0.4.0
  • v1.0.0
  • v1.0.1
  • v1.1.0
  • v1.1.1
  • v1.1.2
  • v1.3.0
  • v1.3.2
  • v1.3.3
  • v1.4.0
16 results
Show changes
Commits on Source (3)
Showing
with 600 additions and 462 deletions
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]
language: python
sudo: required
dist: bionic
dist: focal
python:
- "3.5"
- "3.6"
- "3.7"
- "3.8"
- "3.9"
env:
- FLASK=1.1.2
- FLASK=2.0.2
- FLASK=1.1.4
- FLASK=1.0.4
- FLASK=0.12.5
install:
- pip install Flask==$FLASK
- pip install -r dev_requirements.txt
......
The MIT License (MIT)
Copyright (c) 2019 Alexandre Ferland
Copyright (c) 2022 Alexandre Ferland
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
......
.PHONY: help dev clean update test lint pre-commit
VENV_NAME?=venv
VENV_ACTIVATE=. $(VENV_NAME)/bin/activate
PYTHON=${VENV_NAME}/bin/python3
.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 "make test"
@echo " run tests"
@echo "make lint"
@echo " run black"
@echo "make pre-commit"
@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
test: venv
${PYTHON} -m pytest
lint: venv
$(VENV_NAME)/bin/black -t py310 --exclude $(VENV_NAME) .
pre-commit: venv
$(VENV_NAME)/bin/pre-commit
Flask-SimpleLDAP
Flask-SimpleLDAP [![Build Status](https://app.travis-ci.com/alexferl/flask-simpleldap.svg?branch=master)](https://app.travis-ci.com/alexferl/flask-simpleldap)
================
[![Build Status](https://travis-ci.com/alexferl/flask-simpleldap.svg?branch=master)](https://travis-ci.com/alexferl/flask-simpleldap)
Flask-SimpleLDAP provides LDAP authentication for Flask.
Flask-SimpleLDAP is compatible with and tested on Python 3.5, 3.6 and 3.7.
Flask-SimpleLDAP is compatible with and tested on Python 3.7+.
Quickstart
----------
First, install Flask-SimpleLDAP:
$ pip install flask-simpleldap
```shell
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/).
......@@ -27,19 +28,19 @@ from flask import Flask, g
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'
# 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('/')
@app.route("/")
@ldap.basic_auth_required
def index():
return 'Welcome, {0}!'.format(g.ldap_username)
return "Welcome, {0}!".format(g.ldap_username)
if __name__ == '__main__':
if __name__ == "__main__":
app.run()
```
......@@ -73,31 +74,31 @@ from flask_simpleldap import LDAP
app = Flask(__name__)
# Base
app.config['LDAP_REALM_NAME'] = 'OpenLDAP Authentication'
app.config['LDAP_HOST'] = 'openldap.example.org'
app.config['LDAP_BASE_DN'] = 'dc=users,dc=openldap,dc=org'
app.config['LDAP_USERNAME'] = 'cn=user,ou=servauth-users,dc=users,dc=openldap,dc=org'
app.config['LDAP_PASSWORD'] = 'password'
app.config["LDAP_REALM_NAME"] = "OpenLDAP Authentication"
app.config["LDAP_HOST"] = "openldap.example.org"
app.config["LDAP_BASE_DN"] = "dc=users,dc=openldap,dc=org"
app.config["LDAP_USERNAME"] = "cn=user,ou=servauth-users,dc=users,dc=openldap,dc=org"
app.config["LDAP_PASSWORD"] = "password"
# OpenLDAP
app.config['LDAP_OBJECTS_DN'] = 'dn'
app.config['LDAP_OPENLDAP'] = True
app.config['LDAP_USER_OBJECT_FILTER'] = '(&(objectclass=inetOrgPerson)(uid=%s))'
app.config["LDAP_OBJECTS_DN"] = "dn"
app.config["LDAP_OPENLDAP"] = True
app.config["LDAP_USER_OBJECT_FILTER"] = "(&(objectclass=inetOrgPerson)(uid=%s))"
# Groups
app.config['LDAP_GROUP_MEMBERS_FIELD'] = "uniquemember"
app.config['LDAP_GROUP_OBJECT_FILTER'] = "(&(objectclass=groupOfUniqueNames)(cn=%s))"
app.config['LDAP_GROUP_MEMBER_FILTER'] = "(&(cn=*)(objectclass=groupOfUniqueNames)(uniquemember=%s))"
app.config['LDAP_GROUP_MEMBER_FILTER_FIELD'] = "cn"
app.config["LDAP_GROUP_MEMBERS_FIELD"] = "uniquemember"
app.config["LDAP_GROUP_OBJECT_FILTER"] = "(&(objectclass=groupOfUniqueNames)(cn=%s))"
app.config["LDAP_GROUP_MEMBER_FILTER"] = "(&(cn=*)(objectclass=groupOfUniqueNames)(uniquemember=%s))"
app.config["LDAP_GROUP_MEMBER_FILTER_FIELD"] = "cn"
ldap = LDAP(app)
@app.route('/')
@app.route("/")
@ldap.basic_auth_required
def index():
return 'Welcome, {0}!'.format(g.ldap_username)
return "Welcome, {0}!".format(g.ldap_username)
if __name__ == '__main__':
if __name__ == "__main__":
app.run()
```
......
black==22.1.0
pre-commit==2.17.0
python-ldap==3.2.0 # here instead of requirements.txt so rtfd can build
Sphinx==2.1.2
......
# 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
from pygments.token import (
Keyword,
Name,
Comment,
String,
Error,
Number,
Operator,
Generic,
Whitespace,
Punctuation,
Other,
Literal,
)
class FlaskyStyle(Style):
......@@ -14,10 +26,8 @@ class FlaskyStyle(Style):
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'
......@@ -25,12 +35,9 @@ class FlaskyStyle(Style):
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.
......@@ -53,12 +60,9 @@ class FlaskyStyle(Style):
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'
......@@ -71,7 +75,6 @@ class FlaskyStyle(Style):
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'
......
......@@ -22,13 +22,14 @@ class Mock(MagicMock):
def __getattr__(cls, name):
return Mock()
MOCK_MODULES = ['ldap']
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('..'))
sys.path.insert(0, os.path.abspath(".."))
# -- General configuration -----------
......@@ -39,34 +40,34 @@ sys.path.insert(0, os.path.abspath('..'))
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.intersphinx',
"sphinx.ext.autodoc",
"sphinx.ext.intersphinx",
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# The suffix of source filenames.
source_suffix = '.rst'
source_suffix = ".rst"
# The encoding of source files.
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
master_doc = "index"
# General information about the project.
project = u'Flask-SimpleLDAP'
copyright = u'2019, Alexandre Ferland'
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'
version = "1.4.0"
# The full version, including alpha/beta/rc tags.
release = '1.4.0'
release = "1.4.0"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
......@@ -80,7 +81,7 @@ release = '1.4.0'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
exclude_patterns = ["_build"]
# The reST default role (used for this markup: `text`) to use for all
# documents.
......@@ -98,7 +99,7 @@ exclude_patterns = ['_build']
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
......@@ -109,12 +110,12 @@ pygments_style = 'sphinx'
# -- Options for HTML output ----------------------------------------------
sys.path.append(os.path.abspath('_themes'))
html_theme_path = ['_themes']
html_theme = 'flask_small'
sys.path.append(os.path.abspath("_themes"))
html_theme_path = ["_themes"]
html_theme = "flask_small"
html_theme_options = {
'index_logo': '', #TODO
'github_fork': 'admiralobvious/flask-simpleldap',
"index_logo": "", # TODO
"github_fork": "alexferl/flask-simpleldap",
}
# The name for this set of Sphinx documents. If None, it defaults to
......@@ -136,7 +137,7 @@ html_theme_options = {
# 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']
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
......@@ -185,7 +186,7 @@ html_static_path = ['_static']
# html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'Flask-SimpleLDAPdoc'
htmlhelp_basename = "Flask-SimpleLDAPdoc"
# -- Options for LaTeX output ---------------------------------------------
......@@ -193,10 +194,8 @@ htmlhelp_basename = 'Flask-SimpleLDAPdoc'
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': '',
}
......@@ -205,8 +204,13 @@ latex_elements = {
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'Flask-SimpleLDAP.tex', u'Flask-SimpleLDAP Documentation',
u'Alexandre Ferland', 'manual'),
(
"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
......@@ -235,8 +239,13 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'flask-simpleldap', u'Flask-SimpleLDAP Documentation',
[u'Alexandre Ferland'], 1)
(
"index",
"flask-simpleldap",
"Flask-SimpleLDAP Documentation",
["Alexandre Ferland"],
1,
)
]
# If true, show URL addresses after external links.
......@@ -249,9 +258,15 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'Flask-SimpleLDAP', u'Flask-SimpleLDAP Documentation',
u'Alexandre Ferland', 'Flask-SimpleLDAP', 'One line description of project.',
'Miscellaneous'),
(
"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.
......@@ -268,4 +283,4 @@ texinfo_documents = [
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'http://docs.python.org/': None}
intersphinx_mapping = {"http://docs.python.org/": None}
......@@ -16,11 +16,11 @@ First, install Flask-SimpleLDAP:
.. code-block:: bash
$ pip install flask-simpleldap
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.5, 3.6 and 3.7.
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:
......@@ -31,19 +31,20 @@ required configuration options:
from flask_simpleldap import LDAP
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'
# 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')
@app.route("/ldap")
@ldap.login_required
def ldap_protected():
return 'Success!'
return "Success!"
if __name__ == '__main__':
if __name__ == "__main__":
app.run()
......@@ -122,40 +123,62 @@ History
Changes:
- 1.4.0 July 16, 2019
- This release drops support for `Python 2.7 <https://pythonclock.org/>`_. If you're still on Python 2.7, you can use `v1.3.3 <https://github.com/alexferl/flask-simpleldap/releases/tag/v1.3.3>`_.
- Fixes:
- `#62 <https://github.com/admiralobvious/flask-simpleldap/issues/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 <https://github.com/admiralobvious/flask-simpleldap/pull/51>`_ Referral chasing crash
`#54 <https://github.com/admiralobvious/flask-simpleldap/pull/54>`_ Fixes #44 - Error in bind_user method, also fixes #60 and #61
`#56 <https://github.com/admiralobvious/flask-simpleldap/pull/56>`_ OpenLDAP section has Incorrect LDAP_GROUP_OBJECT_FILTER
`#57 <https://github.com/admiralobvious/flask-simpleldap/pull/57>`_ next vaule: Priority use request.full_path
`#59 <https://github.com/admiralobvious/flask-simpleldap/pull/59>`_ get_object_details to take query_filter and fallback to LDAP_USER_OBJECT_FILTER or LDAP_GROUP_OBJECT_FILTER
- `#51 <https://github.com/admiralobvious/flask-simpleldap/pull/51>`_ Referral chasing crash
- `#54 <https://github.com/admiralobvious/flask-simpleldap/pull/54>`_ Fixes #44 - Error in bind_user method, also fixes #60 and #61
- `#56 <https://github.com/admiralobvious/flask-simpleldap/pull/56>`_ OpenLDAP section has Incorrect LDAP_GROUP_OBJECT_FILTER
- `#57 <https://github.com/admiralobvious/flask-simpleldap/pull/57>`_ next vaule: Priority use request.full_path
- `#59 <https://github.com/admiralobvious/flask-simpleldap/pull/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 <https://github.com/admiralobvious/flask-simpleldap/pull/30>`_,
Fix for python3
- Fix decoding bytes in PY3 for @ldap.group_required.
- 1.1.1 April 10, 2017
- Merge GitHub pull `#26 <https://github.com/admiralobvious/flask-simpleldap/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 <https://github.com/jm66>`_ on GitHub.
- 0.3.0: January 21, 2015
- Fix Github issue `#10 <https://github.com/admiralobvious/flask-simpleldap/issues/10>`_,
Redirect users back to the page they originally requested after authenticating
......@@ -163,14 +186,17 @@ Changes:
Only trust .bind_user() with a non-empty password
- 0.2.0: December 7, 2014
- Added HTTP Basic Authentication. Thanks to `@OptiverTimAll <https://github.com/optivertimall>`_ on GitHub.
- Fix GitHub issue `#4 <https://github.com/admiralobvious/flask-simpleldap/issues/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 <https://github.com/admiralobvious/flask-simpleldap/issues/3>`_,
Not compatible with uppercase distinguished names.
- 0.1: August 9, 2014
- Initial Release
......@@ -3,16 +3,18 @@ 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'
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('/')
@app.route("/")
@ldap.basic_auth_required
def index():
return 'Welcome, {0}!'.format(g.ldap_username)
return "Welcome, {0}!".format(g.ldap_username)
if __name__ == '__main__':
if __name__ == "__main__":
app.run()
......@@ -4,31 +4,35 @@ from flask_simpleldap import LDAP
app = Flask(__name__)
# Base
app.config['LDAP_REALM_NAME'] = 'OpenLDAP Authentication'
app.config['LDAP_HOST'] = 'openldap.example.org'
app.config['LDAP_BASE_DN'] = 'dc=users,dc=openldap,dc=org'
app.config['LDAP_USERNAME'] = 'cn=user,ou=servauth-users,dc=users,dc=openldap,dc=org'
app.config['LDAP_PASSWORD'] = 'password'
app.config["LDAP_REALM_NAME"] = "OpenLDAP Authentication"
app.config["LDAP_HOST"] = "openldap.example.org"
app.config["LDAP_BASE_DN"] = "dc=users,dc=openldap,dc=org"
app.config["LDAP_USERNAME"] = "cn=user,ou=servauth-users,dc=users,dc=openldap,dc=org"
app.config["LDAP_PASSWORD"] = "password"
# OpenLDAP
app.config['LDAP_OPENLDAP'] = True
app.config['LDAP_OBJECTS_DN'] = 'dn'
app.config['LDAP_USER_OBJECT_FILTER'] = '(&(objectclass=inetOrgPerson)(uid=%s))'
app.config["LDAP_OPENLDAP"] = True
app.config["LDAP_OBJECTS_DN"] = "dn"
app.config["LDAP_USER_OBJECT_FILTER"] = "(&(objectclass=inetOrgPerson)(uid=%s))"
# Groups configuration
app.config['LDAP_GROUP_MEMBERS_FIELD'] = 'uniquemember'
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_FIELD'] = "cn"
app.config["LDAP_GROUP_MEMBERS_FIELD"] = "uniquemember"
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_FIELD"] = "cn"
ldap = LDAP(app)
@app.route('/')
@app.route("/")
@ldap.basic_auth_required
def index():
return 'Welcome, {0}!'.format(g.ldap_username)
return "Welcome, {0}!".format(g.ldap_username)
if __name__ == '__main__':
if __name__ == "__main__":
app.run()
......@@ -5,10 +5,7 @@ from .extensions import ldap
from .core import core
from .foo import foo
DEFAULT_BLUEPRINTS = (
core,
foo
)
DEFAULT_BLUEPRINTS = (core, foo)
def create_app(config=None, app_name=None, blueprints=None):
......@@ -33,11 +30,11 @@ def register_hooks(app):
@app.before_request
def before_request():
g.user = None
if 'user_id' in session:
if "user_id" in session:
# This is where you'd query your database to get the user info.
g.user = {}
# Create a global with the LDAP groups the user is a member of.
g.ldap_groups = ldap.get_user_groups(user=session['user_id'])
g.ldap_groups = ldap.get_user_groups(user=session["user_id"])
def register_blueprints(app, blueprints):
......
......@@ -2,14 +2,14 @@ import ldap
class BaseConfig(object):
PROJECT = 'foo'
SECRET_KEY = 'dev key'
PROJECT = "foo"
SECRET_KEY = "dev key"
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_LOGIN_VIEW = 'core.login'
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_LOGIN_VIEW = "core.login"
LDAP_CUSTOM_OPTIONS = {ldap.OPT_REFERRALS: 0}
from flask import Blueprint, g, request, session, redirect, url_for
from ..extensions import ldap
core = Blueprint('core', __name__)
core = Blueprint("core", __name__)
@core.route('/')
@core.route("/")
@ldap.login_required
def index():
return 'Successfully logged in!'
return "Successfully logged in!"
@core.route('/login', methods=['GET', 'POST'])
@core.route("/login", methods=["GET", "POST"])
def login():
if g.user:
return redirect(url_for('index'))
if request.method == 'POST':
user = request.form['user']
passwd = request.form['passwd']
return redirect(url_for("index"))
if request.method == "POST":
user = request.form["user"]
passwd = request.form["passwd"]
test = ldap.bind_user(user, passwd)
if test is None or passwd == '':
return 'Invalid credentials'
if test is None or passwd == "":
return "Invalid credentials"
else:
session['user_id'] = request.form['user']
return redirect('/')
session["user_id"] = request.form["user"]
return redirect("/")
return """<form action="" method="post">
user: <input name="user"><br>
password:<input type="password" name="passwd"><br>
<input type="submit" value="Submit"></form>"""
@core.route('/group')
@ldap.group_required(groups=['Web Developers', 'QA'])
@core.route("/group")
@ldap.group_required(groups=["Web Developers", "QA"])
def group():
return 'Group restricted page'
return "Group restricted page"
@core.route('/logout')
@core.route("/logout")
def logout():
session.pop('user_id', None)
return redirect(url_for('index'))
session.pop("user_id", None)
return redirect(url_for("index"))
from flask_simpleldap import LDAP
ldap = LDAP()
from flask import Blueprint
from ..extensions import ldap
foo = Blueprint('foo', __name__, url_prefix='/foo')
foo = Blueprint("foo", __name__, url_prefix="/foo")
@foo.route('/group')
@ldap.group_required(groups=['Web Developers', 'QA'])
@foo.route("/group")
@ldap.group_required(groups=["Web Developers", "QA"])
def group():
return 'Group restricted page in foo module'
return "Group restricted page in foo module"
......@@ -3,14 +3,14 @@ from flask import Flask, g, request, session, redirect, url_for
from flask_simpleldap import LDAP
app = Flask(__name__)
app.secret_key = 'dev key'
app.secret_key = "dev key"
app.debug = True
app.config['LDAP_HOST'] = 'ldap.example.org'
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'
app.config['LDAP_CUSTOM_OPTIONS'] = {l.OPT_REFERRALS: 0}
app.config["LDAP_HOST"] = "ldap.example.org"
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"
app.config["LDAP_CUSTOM_OPTIONS"] = {l.OPT_REFERRALS: 0}
ldap = LDAP(app)
......@@ -18,49 +18,49 @@ ldap = LDAP(app)
@app.before_request
def before_request():
g.user = None
if 'user_id' in session:
if "user_id" in session:
# This is where you'd query your database to get the user info.
g.user = {}
# Create a global with the LDAP groups the user is a member of.
g.ldap_groups = ldap.get_user_groups(user=session['user_id'])
g.ldap_groups = ldap.get_user_groups(user=session["user_id"])
@app.route('/')
@app.route("/")
@ldap.login_required
def index():
return 'Successfully logged in!'
return "Successfully logged in!"
@app.route('/login', methods=['GET', 'POST'])
@app.route("/login", methods=["GET", "POST"])
def login():
if g.user:
return redirect(url_for('index'))
if request.method == 'POST':
user = request.form['user']
passwd = request.form['passwd']
return redirect(url_for("index"))
if request.method == "POST":
user = request.form["user"]
passwd = request.form["passwd"]
test = ldap.bind_user(user, passwd)
if test is None or passwd == '':
return 'Invalid credentials'
if test is None or passwd == "":
return "Invalid credentials"
else:
session['user_id'] = request.form['user']
return redirect('/')
session["user_id"] = request.form["user"]
return redirect("/")
return """<form action="" method="post">
user: <input name="user"><br>
password:<input type="password" name="passwd"><br>
<input type="submit" value="Submit"></form>"""
@app.route('/group')
@ldap.group_required(groups=['Web Developers', 'QA'])
@app.route("/group")
@ldap.group_required(groups=["Web Developers", "QA"])
def group():
return 'Group restricted page'
return "Group restricted page"
@app.route('/logout')
@app.route("/logout")
def logout():
session.pop('user_id', None)
return redirect(url_for('index'))
session.pop("user_id", None)
return redirect(url_for("index"))
if __name__ == '__main__':
if __name__ == "__main__":
app.run()
......@@ -2,25 +2,27 @@ from flask import Flask, g, request, session, redirect, url_for
from flask_simpleldap import LDAP
app = Flask(__name__)
app.secret_key = 'dev key'
app.secret_key = "dev key"
app.debug = True
app.config['LDAP_OPENLDAP'] = True
app.config['LDAP_OBJECTS_DN'] = 'dn'
app.config['LDAP_REALM_NAME'] = 'OpenLDAP Authentication'
app.config['LDAP_HOST'] = 'openldap.example.org'
app.config['LDAP_BASE_DN'] = 'dc=users,dc=openldap,dc=org'
app.config['LDAP_USERNAME'] = 'cn=user,ou=servauth-users,dc=users,dc=openldap,dc=org'
app.config['LDAP_PASSWORD'] = 'password'
app.config['LDAP_USER_OBJECT_FILTER'] = '(&(objectclass=inetOrgPerson)(uid=%s))'
app.config["LDAP_OPENLDAP"] = True
app.config["LDAP_OBJECTS_DN"] = "dn"
app.config["LDAP_REALM_NAME"] = "OpenLDAP Authentication"
app.config["LDAP_HOST"] = "openldap.example.org"
app.config["LDAP_BASE_DN"] = "dc=users,dc=openldap,dc=org"
app.config["LDAP_USERNAME"] = "cn=user,ou=servauth-users,dc=users,dc=openldap,dc=org"
app.config["LDAP_PASSWORD"] = "password"
app.config["LDAP_USER_OBJECT_FILTER"] = "(&(objectclass=inetOrgPerson)(uid=%s))"
# Group configuration
app.config['LDAP_GROUP_MEMBERS_FIELD'] = 'uniquemember'
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_FIELD'] = "cn"
app.config["LDAP_GROUP_MEMBERS_FIELD"] = "uniquemember"
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_FIELD"] = "cn"
ldap = LDAP(app)
......@@ -28,49 +30,49 @@ ldap = LDAP(app)
@app.before_request
def before_request():
g.user = None
if 'user_id' in session:
if "user_id" in session:
# This is where you'd query your database to get the user info.
g.user = {}
# Create a global with the LDAP groups the user is a member of.
g.ldap_groups = ldap.get_user_groups(user=session['user_id'])
g.ldap_groups = ldap.get_user_groups(user=session["user_id"])
@app.route('/')
@app.route("/")
@ldap.login_required
def index():
return 'Successfully logged in!'
return "Successfully logged in!"
@app.route('/login', methods=['GET', 'POST'])
@app.route("/login", methods=["GET", "POST"])
def login():
if g.user:
return redirect(url_for('index'))
if request.method == 'POST':
user = request.form['user']
passwd = request.form['passwd']
return redirect(url_for("index"))
if request.method == "POST":
user = request.form["user"]
passwd = request.form["passwd"]
test = ldap.bind_user(user, passwd)
if test is None or passwd == '':
return 'Invalid credentials'
if test is None or passwd == "":
return "Invalid credentials"
else:
session['user_id'] = request.form['user']
return redirect('/')
session["user_id"] = request.form["user"]
return redirect("/")
return """<form action="" method="post">
user: <input name="user"><br>
password:<input type="password" name="passwd"><br>
<input type="submit" value="Submit"></form>"""
@app.route('/group')
@ldap.group_required(groups=['web-developers'])
@app.route("/group")
@ldap.group_required(groups=["web-developers"])
def group():
return 'Group restricted page'
return "Group restricted page"
@app.route('/logout')
@app.route("/logout")
def logout():
session.pop('user_id', None)
return redirect(url_for('index'))
session.pop("user_id", None)
return redirect(url_for("index"))
if __name__ == '__main__':
if __name__ == "__main__":
app.run()
......@@ -2,10 +2,9 @@ import re
from functools import wraps
import ldap
from ldap import filter as ldap_filter
from flask import abort, current_app, g, make_response, redirect, url_for, \
request
from flask import abort, current_app, g, make_response, redirect, url_for, request
__all__ = ['LDAP']
__all__ = ["LDAP"]
class LDAPException(RuntimeError):
......@@ -32,51 +31,50 @@ class LDAP(object):
:param flask.Flask app: the application to configure for use with
this :class:`~LDAP`
"""
app.config.setdefault('LDAP_HOST', 'localhost')
app.config.setdefault('LDAP_PORT', 389)
app.config.setdefault('LDAP_SCHEMA', 'ldap')
app.config.setdefault('LDAP_USERNAME', None)
app.config.setdefault('LDAP_PASSWORD', None)
app.config.setdefault('LDAP_TIMEOUT', 10)
app.config.setdefault('LDAP_USE_SSL', False)
app.config.setdefault('LDAP_USE_TLS', False)
app.config.setdefault('LDAP_REQUIRE_CERT', False)
app.config.setdefault('LDAP_CERT_PATH', '/path/to/cert')
app.config.setdefault('LDAP_BASE_DN', None)
app.config.setdefault('LDAP_OBJECTS_DN', 'distinguishedName')
app.config.setdefault('LDAP_USER_FIELDS', [])
app.config.setdefault('LDAP_USER_OBJECT_FILTER',
'(&(objectclass=Person)(userPrincipalName=%s))')
app.config.setdefault('LDAP_USER_GROUPS_FIELD', 'memberOf')
app.config.setdefault('LDAP_GROUP_FIELDS', [])
app.config.setdefault('LDAP_GROUPS_OBJECT_FILTER', 'objectclass=Group')
app.config.setdefault('LDAP_GROUP_OBJECT_FILTER',
'(&(objectclass=Group)(userPrincipalName=%s))')
app.config.setdefault('LDAP_GROUP_MEMBERS_FIELD', 'member')
app.config.setdefault('LDAP_LOGIN_VIEW', 'login')
app.config.setdefault('LDAP_REALM_NAME', 'LDAP authentication')
app.config.setdefault('LDAP_OPENLDAP', False)
app.config.setdefault('LDAP_GROUP_MEMBER_FILTER', '*')
app.config.setdefault('LDAP_GROUP_MEMBER_FILTER_FIELD', '*')
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)
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'])
for option in ['USERNAME', 'PASSWORD', 'BASE_DN']:
if app.config['LDAP_{0}'.format(option)] is None:
raise LDAPException('LDAP_{0} cannot be None!'.format(option))
app.config.setdefault("LDAP_HOST", "localhost")
app.config.setdefault("LDAP_PORT", 389)
app.config.setdefault("LDAP_SCHEMA", "ldap")
app.config.setdefault("LDAP_USERNAME", None)
app.config.setdefault("LDAP_PASSWORD", None)
app.config.setdefault("LDAP_TIMEOUT", 10)
app.config.setdefault("LDAP_USE_SSL", False)
app.config.setdefault("LDAP_USE_TLS", False)
app.config.setdefault("LDAP_REQUIRE_CERT", False)
app.config.setdefault("LDAP_CERT_PATH", "/path/to/cert")
app.config.setdefault("LDAP_BASE_DN", None)
app.config.setdefault("LDAP_OBJECTS_DN", "distinguishedName")
app.config.setdefault("LDAP_USER_FIELDS", [])
app.config.setdefault(
"LDAP_USER_OBJECT_FILTER", "(&(objectclass=Person)(userPrincipalName=%s))"
)
app.config.setdefault("LDAP_USER_GROUPS_FIELD", "memberOf")
app.config.setdefault("LDAP_GROUP_FIELDS", [])
app.config.setdefault("LDAP_GROUPS_OBJECT_FILTER", "objectclass=Group")
app.config.setdefault(
"LDAP_GROUP_OBJECT_FILTER", "(&(objectclass=Group)(userPrincipalName=%s))"
)
app.config.setdefault("LDAP_GROUP_MEMBERS_FIELD", "member")
app.config.setdefault("LDAP_LOGIN_VIEW", "login")
app.config.setdefault("LDAP_REALM_NAME", "LDAP authentication")
app.config.setdefault("LDAP_OPENLDAP", False)
app.config.setdefault("LDAP_GROUP_MEMBER_FILTER", "*")
app.config.setdefault("LDAP_GROUP_MEMBER_FILTER_FIELD", "*")
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)
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"])
for option in ["USERNAME", "PASSWORD", "BASE_DN"]:
if app.config["LDAP_{0}".format(option)] is None:
raise LDAPException("LDAP_{0} cannot be None!".format(option))
@staticmethod
def _set_custom_options(conn):
options = current_app.config['LDAP_CUSTOM_OPTIONS']
options = current_app.config["LDAP_CUSTOM_OPTIONS"]
if options:
for k, v in options.items():
conn.set_option(k, v)
......@@ -90,15 +88,19 @@ class LDAP(object):
"""
try:
conn = ldap.initialize('{0}://{1}:{2}'.format(
current_app.config['LDAP_SCHEMA'],
current_app.config['LDAP_HOST'],
current_app.config['LDAP_PORT']))
conn.set_option(ldap.OPT_NETWORK_TIMEOUT,
current_app.config['LDAP_TIMEOUT'])
conn = ldap.initialize(
"{0}://{1}:{2}".format(
current_app.config["LDAP_SCHEMA"],
current_app.config["LDAP_HOST"],
current_app.config["LDAP_PORT"],
)
)
conn.set_option(
ldap.OPT_NETWORK_TIMEOUT, current_app.config["LDAP_TIMEOUT"]
)
conn = self._set_custom_options(conn)
conn.protocol_version = ldap.VERSION3
if current_app.config['LDAP_USE_TLS']:
if current_app.config["LDAP_USE_TLS"]:
conn.start_tls_s()
return conn
except ldap.LDAPError as e:
......@@ -116,8 +118,8 @@ class LDAP(object):
conn = self.initialize
try:
conn.simple_bind_s(
current_app.config['LDAP_USERNAME'],
current_app.config['LDAP_PASSWORD'])
current_app.config["LDAP_USERNAME"], current_app.config["LDAP_PASSWORD"]
)
return conn
except ldap.LDAPError as e:
raise LDAPException(self.error(e.args))
......@@ -148,15 +150,17 @@ class LDAP(object):
return
try:
conn = self.initialize
_user_dn = user_dn.decode('utf-8') \
if isinstance(user_dn, bytes) else user_dn
_user_dn = (
user_dn.decode("utf-8") if isinstance(user_dn, bytes) else user_dn
)
conn.simple_bind_s(_user_dn, password)
return True
except ldap.LDAPError:
return
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.
......@@ -169,33 +173,35 @@ class LDAP(object):
fields = None
if user is not None:
if not dn_only:
fields = current_app.config['LDAP_USER_FIELDS']
query_filter = query_filter or \
current_app.config['LDAP_USER_OBJECT_FILTER']
fields = current_app.config["LDAP_USER_FIELDS"]
query_filter = query_filter or current_app.config["LDAP_USER_OBJECT_FILTER"]
query = ldap_filter.filter_format(query_filter, (user,))
elif group is not None:
if not dn_only:
fields = current_app.config['LDAP_GROUP_FIELDS']
query_filter = query_filter or \
current_app.config['LDAP_GROUP_OBJECT_FILTER']
fields = current_app.config["LDAP_GROUP_FIELDS"]
query_filter = (
query_filter or current_app.config["LDAP_GROUP_OBJECT_FILTER"]
)
query = ldap_filter.filter_format(query_filter, (group,))
conn = self.bind
try:
records = conn.search_s(current_app.config['LDAP_BASE_DN'],
ldap.SCOPE_SUBTREE, query, fields)
records = conn.search_s(
current_app.config["LDAP_BASE_DN"], ldap.SCOPE_SUBTREE, query, fields
)
conn.unbind_s()
result = {}
if records and\
records[0][0] is not None and isinstance(records[0][1], dict):
if (
records
and records[0][0] is not None
and isinstance(records[0][1], dict)
):
if dn_only:
if current_app.config['LDAP_OPENLDAP']:
if current_app.config["LDAP_OPENLDAP"]:
if records:
return records[0][0]
else:
if current_app.config['LDAP_OBJECTS_DN'] \
in records[0][1]:
dn = records[0][1][
current_app.config['LDAP_OBJECTS_DN']]
if current_app.config["LDAP_OBJECTS_DN"] in records[0][1]:
dn = records[0][1][current_app.config["LDAP_OBJECTS_DN"]]
return dn[0]
for k, v in list(records[0][1].items()):
result[k] = v
......@@ -217,17 +223,21 @@ class LDAP(object):
"""
conn = self.bind
try:
fields = fields or current_app.config['LDAP_GROUP_FIELDS']
if current_app.config['LDAP_OPENLDAP']:
fields = fields or current_app.config["LDAP_GROUP_FIELDS"]
if current_app.config["LDAP_OPENLDAP"]:
records = conn.search_s(
current_app.config['LDAP_BASE_DN'], ldap.SCOPE_SUBTREE,
current_app.config['LDAP_GROUPS_OBJECT_FILTER'],
fields)
current_app.config["LDAP_BASE_DN"],
ldap.SCOPE_SUBTREE,
current_app.config["LDAP_GROUPS_OBJECT_FILTER"],
fields,
)
else:
records = conn.search_s(
current_app.config['LDAP_BASE_DN'], ldap.SCOPE_SUBTREE,
current_app.config['LDAP_GROUPS_OBJECT_FILTER'],
fields)
current_app.config["LDAP_BASE_DN"],
ldap.SCOPE_SUBTREE,
current_app.config["LDAP_GROUPS_OBJECT_FILTER"],
fields,
)
conn.unbind_s()
if records:
if dn_only:
......@@ -248,42 +258,51 @@ class LDAP(object):
conn = self.bind
try:
if current_app.config['LDAP_OPENLDAP']:
fields = \
[str(current_app.config['LDAP_GROUP_MEMBER_FILTER_FIELD'])]
if current_app.config["LDAP_OPENLDAP"]:
fields = [str(current_app.config["LDAP_GROUP_MEMBER_FILTER_FIELD"])]
records = conn.search_s(
current_app.config['LDAP_BASE_DN'], ldap.SCOPE_SUBTREE,
current_app.config["LDAP_BASE_DN"],
ldap.SCOPE_SUBTREE,
ldap_filter.filter_format(
current_app.config['LDAP_GROUP_MEMBER_FILTER'],
(self.get_object_details(user, dn_only=True),)),
fields)
current_app.config["LDAP_GROUP_MEMBER_FILTER"],
(self.get_object_details(user, dn_only=True),),
),
fields,
)
else:
records = conn.search_s(
current_app.config['LDAP_BASE_DN'], ldap.SCOPE_SUBTREE,
current_app.config["LDAP_BASE_DN"],
ldap.SCOPE_SUBTREE,
ldap_filter.filter_format(
current_app.config['LDAP_USER_OBJECT_FILTER'],
(user,)),
[current_app.config['LDAP_USER_GROUPS_FIELD']])
current_app.config["LDAP_USER_OBJECT_FILTER"], (user,)
),
[current_app.config["LDAP_USER_GROUPS_FIELD"]],
)
conn.unbind_s()
if records:
if current_app.config['LDAP_OPENLDAP']:
group_member_filter = \
current_app.config['LDAP_GROUP_MEMBER_FILTER_FIELD']
if current_app.config["LDAP_OPENLDAP"]:
group_member_filter = current_app.config[
"LDAP_GROUP_MEMBER_FILTER_FIELD"
]
record_list = [record[1] for record in records]
record_dicts = [
record for record in record_list if isinstance(record, dict)]
groups = [item.get([group_member_filter][0])[0]
for item in record_dicts]
record for record in record_list if isinstance(record, dict)
]
groups = [
item.get([group_member_filter][0])[0] for item in record_dicts
]
return groups
else:
if current_app.config['LDAP_USER_GROUPS_FIELD'] in \
records[0][1]:
if current_app.config["LDAP_USER_GROUPS_FIELD"] in records[0][1]:
groups = records[0][1][
current_app.config['LDAP_USER_GROUPS_FIELD']]
result = [re.findall(b'(?:cn=|CN=)(.*?),', group)[0]
for group in groups]
result = [r.decode('utf-8') for r in result]
current_app.config["LDAP_USER_GROUPS_FIELD"]
]
result = [
re.findall(b"(?:cn=|CN=)(.*?),", group)[0]
for group in groups
]
result = [r.decode("utf-8") for r in result]
return result
except ldap.LDAPError as e:
raise LDAPException(self.error(e.args))
......@@ -298,17 +317,20 @@ class LDAP(object):
conn = self.bind
try:
records = conn.search_s(
current_app.config['LDAP_BASE_DN'], ldap.SCOPE_SUBTREE,
current_app.config["LDAP_BASE_DN"],
ldap.SCOPE_SUBTREE,
ldap_filter.filter_format(
current_app.config['LDAP_GROUP_OBJECT_FILTER'], (group,)),
[current_app.config['LDAP_GROUP_MEMBERS_FIELD']])
current_app.config["LDAP_GROUP_OBJECT_FILTER"], (group,)
),
[current_app.config["LDAP_GROUP_MEMBERS_FIELD"]],
)
conn.unbind_s()
if records:
if current_app.config['LDAP_GROUP_MEMBERS_FIELD'] in \
records[0][1]:
if current_app.config["LDAP_GROUP_MEMBERS_FIELD"] in records[0][1]:
members = records[0][1][
current_app.config['LDAP_GROUP_MEMBERS_FIELD']]
members = [m.decode('utf-8') for m in members]
current_app.config["LDAP_GROUP_MEMBERS_FIELD"]
]
members = [m.decode("utf-8") for m in members]
return members
except ldap.LDAPError as e:
raise LDAPException(self.error(e.args))
......@@ -316,8 +338,8 @@ class LDAP(object):
@staticmethod
def error(e):
e = e[0]
if 'desc' in e:
return e['desc']
if "desc" in e:
return e["desc"]
else:
return e
......@@ -338,11 +360,11 @@ class LDAP(object):
def wrapped(*args, **kwargs):
if g.user is None:
next_path = request.full_path or request.path
if next_path == '/?':
if next_path == "/?":
return redirect(url_for(current_app.config["LDAP_LOGIN_VIEW"]))
return redirect(
url_for(current_app.config['LDAP_LOGIN_VIEW']))
return redirect(url_for(current_app.config['LDAP_LOGIN_VIEW'],
next=next_path))
url_for(current_app.config["LDAP_LOGIN_VIEW"], next=next_path)
)
return func(*args, **kwargs)
return wrapped
......@@ -367,8 +389,11 @@ class LDAP(object):
def wrapped(*args, **kwargs):
if g.user is None:
return redirect(
url_for(current_app.config['LDAP_LOGIN_VIEW'],
next=request.full_path or request.path))
url_for(
current_app.config["LDAP_LOGIN_VIEW"],
next=request.full_path or request.path,
)
)
match = [group for group in groups if group in g.ldap_groups]
if not match:
abort(403)
......@@ -395,9 +420,8 @@ class LDAP(object):
"""
def make_auth_required_response():
response = make_response('Unauthorized', 401)
response.www_authenticate.set_basic(
current_app.config['LDAP_REALM_NAME'])
response = make_response("Unauthorized", 401)
response.www_authenticate.set_basic(current_app.config["LDAP_REALM_NAME"])
return response
@wraps(func)
......@@ -412,13 +436,14 @@ class LDAP(object):
# with an empty password, even if you supply a non-anonymous user
# ID, causing .bind_user() to return True. Therefore, only accept
# non-empty passwords.
if req_username in ['', None] or req_password in ['', None]:
current_app.logger.debug('Got a request without auth data')
if req_username in ["", None] or req_password in ["", None]:
current_app.logger.debug("Got a request without auth data")
return make_auth_required_response()
if not self.bind_user(req_username, req_password):
current_app.logger.debug('User {0!r} gave wrong '
'password'.format(req_username))
current_app.logger.debug(
"User {0!r} gave wrong " "password".format(req_username)
)
return make_auth_required_response()
g.ldap_username = req_username
......
Flask==1.1.1
mock==3.0.5 # for ci
Flask==2.0.2
mock==4.0.3 # for ci