Codebase list flask-ldapconn / run/993ddc34-b8a5-4334-afcb-d2ffec35147f/upstream
Import upstream version 0.10.1+git20210602.1.46bf8ed Debian Janitor 1 year, 4 months ago
9 changed file(s) with 597 addition(s) and 571 deletion(s). Raw diff Collapse all Expand all
00 Changelog
11 =========
2
3 0.10.1 (2010-12-23)
4 -------------------
5
6 * Fix security issue: allows authentication without password (Roland)
7
8 0.10.0 (2019-10-20)
9 -------------------
10
11 * End support for Python 2.7
12 * fix adding zero integer attribute value (HAMANO Tsukasa)
13
14 0.9.0 (2019-08-17)
15 ------------------
16
17 * Fix anonymous binding where no security layer is need at all (Matthias Tafelmeier @cherusk)
18
19 0.8.0 (2019-05-09)
20 ------------------
21
22 * Refactored LDAPAttribute class (Alexei Margasov @alexei38)
23 * Add support for Python 3.7
24 * End support for Python 3.4
25 * Update requirements in Pipfile.lock
226
327 0.7.2 (2018-06-14)
428 ------------------
0 Metadata-Version: 1.1
0 Metadata-Version: 2.1
11 Name: Flask-LDAPConn
2 Version: 0.7.2
2 Version: 0.10.1
33 Summary: Pure python, LDAP connection and ORM for Flask Applications
44 Home-page: http://github.com/rroemhild/flask-ldapconn
55 Author: Rafael Römhild
66 Author-email: rafael@roemhild.de
77 License: BSD
8 Description: Flask-LDAPConn
9 ==============
10
11 .. image:: https://travis-ci.org/rroemhild/flask-ldapconn.svg?branch=master
12 :target: https://travis-ci.org/rroemhild/flask-ldapconn
13
14 .. image:: https://badge.fury.io/py/Flask-LDAPConn.svg
15 :target: https://badge.fury.io/py/Flask-LDAPConn
16
17 Flask-LDAPConn is a Flask extension providing `ldap3 <https://github.com/cannatag/ldap3>`_ (an LDAP V3 pure Python client) connection for accessing LDAP servers.
18
19 To abstract access to LDAP data this extension provides a simple ORM model.
20
21
22 Installation
23 ------------
24
25 .. code-block:: shell
26
27 pip install flask-ldapconn
28
29
30 Configuration
31 -------------
32
33 Your configuration should be declared within your Flask config. Sample configuration:
34
35 .. code-block:: python
36
37 import ssl
38
39 LDAP_SERVER = 'localhost'
40 LDAP_PORT = 389
41 LDAP_BINDDN = 'cn=admin,dc=example,dc=com'
42 LDAP_SECRET = 'forty-two'
43 LDAP_CONNECT_TIMEOUT = 10 # Honored when the TCP connection is being established
44 LDAP_USE_TLS = True # default
45 LDAP_REQUIRE_CERT = ssl.CERT_NONE # default: CERT_REQUIRED
46 LDAP_TLS_VERSION = ssl.PROTOCOL_TLSv1_2 # default: PROTOCOL_TLSv1
47 LDAP_CERT_PATH = '/etc/openldap/certs'
48
49 If you want to always get any entry attribute value as a list, instead of a string if only one item is in the attribute list, then set:
50
51 .. code-block:: python
52
53 FORCE_ATTRIBUTE_VALUE_AS_LIST = True
54
55 Default is ``False`` and will return a string if only one item is in the attribute list.
56
57
58 Setup
59 -----
60
61 Create the LDAP instance in your application.
62
63 .. code-block:: python
64
65 from flask import Flask
66 from flask_ldapconn import LDAPConn
67
68 app = Flask(__name__)
69 ldap = LDAPConn(app)
70
71
72 Client sample
73 -------------
74
75 .. code-block:: python
76
77 from flask import Flask
78 from flask_ldapconn import LDAPConn
79 from ldap3 import SUBTREE
80
81 app = Flask(__name__)
82 ldap = LDAPConn(app)
83
84 @app.route('/')
85 def index():
86 ldapc = ldap.connection
87 basedn = 'ou=people,dc=example,dc=com'
88 search_filter = '(objectClass=posixAccount)'
89 attributes = ['sn', 'givenName', 'uid', 'mail']
90 ldapc.search(basedn, search_filter, SUBTREE,
91 attributes=attributes)
92 response = ldapc.response
93
94
95 User model samples
96 ------------------
97
98 .. code-block:: python
99
100 from flask import Flask
101 from flask_ldapconn import LDAPConn
102
103 app = Flask(__name__)
104 ldap = LDAPConn(app)
105
106 class User(ldap.Entry):
107
108 base_dn = 'ou=people,dc=example,dc=com'
109 object_classes = ['inetOrgPerson']
110
111 name = ldap.Attribute('cn')
112 email = ldap.Attribute('mail')
113 userid = ldap.Attribute('uid')
114 surname = ldap.Attribute('sn')
115 givenname = ldap.Attribute('givenName')
116
117 with app.app_context():
118
119 # get a list of entries
120 entries = User.query.filter('email: *@example.com').all()
121 for entry in entries:
122 print u'Name: {}'.format(entry.name)
123
124 # get the first entry
125 user = User.query.filter('userid: user1').first()
126
127 # new entry
128 new_user = User(
129 name='User Three',
130 email='user3@example.com',
131 userid='user3',
132 surname='Three',
133 givenname='User'
134 )
135 new_user.save()
136
137 # modify entry
138 mod_user = User.query.filter('userid: user1').first()
139 mod_user.name = 'User Number Three'
140 mod_user.email.append.('u.three@example.com')
141 mod_user.givenname.delete()
142 mod_user.save()
143
144 # remove entry
145 rm_user = User.query.filter('userid: user1').first()
146 rm_user.delete()
147
148 # authenticate user
149 auth_user = User.query.filter('userid: user1').first()
150 if auth_user:
151 if auth_user.authenticate('password1234'):
152 print('Authenticated')
153 else:
154 print('Wrong password')
155
156
157 Authenticate with Client
158 ------------------------
159
160 .. code-block:: python
161
162 from flask import Flask
163 from flask_ldapconn import LDAPConn
164
165 app = Flask(__name__)
166 ldap = LDAPConn(app)
167
168 username = 'user1'
169 password = 'userpass'
170 attribute = 'uid'
171 search_filter = ('(active=1)')
172
173 with app.app_context():
174 retval = ldap.authenticate(username, password, attribute,
175 basedn, search_filter)
176 if not retval:
177 return 'Invalid credentials.'
178 return 'Welcome %s.' % username
179
180
181 Bind as user
182 ------------
183
184 To bind as user for the current request instance a new connection from ``flask.g.ldap_conn``:
185
186 .. code-block:: python
187
188 g.ldap_conn = ldap.connect(userdn, password)
189 user = User.query.get(userdn)
190
191
192 Unit Test
193 ---------
194
195 I use a simple Docker image to run the tests on localhost. The test file ``test_flask_ldapconn.py`` tries to handle ``start`` and ``stop`` of the docker container:
196
197 .. code-block:: shell
198
199 pip install docker-py
200 docker pull rroemhild/test-openldap
201 python test_flask_ldapconn.py
202
203 Run the docker container manual:
204
205 .. code-block:: shell
206
207 docker run --privileged -d -p 389:389 --name flask_ldapconn rroemhild/test-openldap
208 DOCKER_RUN=False python test_flask_ldapconn.py
209
210 Unit test with your own settings from a file:
211
212 .. code-block:: shell
213
214 LDAP_SETTINGS=my_settings.py python test_flask_ldapconn.py
215
216
217 Contribute
218 ----------
219
220 #. Check for open issues or open a fresh issue to start a discussion around a feature idea or a bug.
221 #. Fork `the repository`_ on Github to start making your changes.
222 #. Write a test which shows that the bug was fixed or that the feature works as expected.
223 #. Send a pull request and bug the maintainer until it gets merged and published.
224
225 .. _`the repository`: http://github.com/rroemhild/flask-ldapconn
226
2278 Keywords: flask ldap ldap3 orm
2289 Platform: any
229 Classifier: Development Status :: 4 - Beta
10 Classifier: Development Status :: 5 - Production/Stable
23011 Classifier: Environment :: Web Environment
23112 Classifier: Intended Audience :: Developers
23213 Classifier: License :: OSI Approved :: BSD License
23314 Classifier: Operating System :: OS Independent
234 Classifier: Programming Language :: Python :: 2.7
235 Classifier: Programming Language :: Python :: 3.4
23615 Classifier: Programming Language :: Python :: 3.5
23716 Classifier: Programming Language :: Python :: 3.6
17 Classifier: Programming Language :: Python :: 3.7
23818 Classifier: Framework :: Flask
23919 Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
24020 Classifier: Topic :: Software Development :: Libraries :: Python Modules
21 License-File: LICENSE
22
23 Flask-LDAPConn
24 ==============
25
26 .. image:: https://travis-ci.org/rroemhild/flask-ldapconn.svg?branch=master
27 :target: https://travis-ci.org/rroemhild/flask-ldapconn
28
29 .. image:: https://badge.fury.io/py/Flask-LDAPConn.svg
30 :target: https://badge.fury.io/py/Flask-LDAPConn
31
32 Flask-LDAPConn is a Flask extension providing `ldap3 <https://github.com/cannatag/ldap3>`_ (an LDAP V3 pure Python client) connection for accessing LDAP servers.
33
34 To abstract access to LDAP data this extension provides a simple ORM model.
35
36
37 Installation
38 ------------
39
40 .. code-block:: shell
41
42 pip install flask-ldapconn
43
44
45 Configuration
46 -------------
47
48 Your configuration should be declared within your Flask config. Sample configuration:
49
50 .. code-block:: python
51
52 import ssl
53
54 LDAP_SERVER = 'localhost'
55 LDAP_PORT = 389
56 LDAP_BINDDN = 'cn=admin,dc=example,dc=com'
57 LDAP_SECRET = 'forty-two'
58 LDAP_CONNECT_TIMEOUT = 10 # Honored when the TCP connection is being established
59 LDAP_USE_TLS = True # default
60 LDAP_REQUIRE_CERT = ssl.CERT_NONE # default: CERT_REQUIRED
61 LDAP_TLS_VERSION = ssl.PROTOCOL_TLSv1_2 # default: PROTOCOL_TLSv1
62 LDAP_CERT_PATH = '/etc/openldap/certs'
63
64 If you want to always get any entry attribute value as a list, instead of a string if only one item is in the attribute list, then set:
65
66 .. code-block:: python
67
68 FORCE_ATTRIBUTE_VALUE_AS_LIST = True
69
70 Default is ``False`` and will return a string if only one item is in the attribute list.
71
72
73 Setup
74 -----
75
76 Create the LDAP instance in your application.
77
78 .. code-block:: python
79
80 from flask import Flask
81 from flask_ldapconn import LDAPConn
82
83 app = Flask(__name__)
84 ldap = LDAPConn(app)
85
86
87 Client sample
88 -------------
89
90 .. code-block:: python
91
92 from flask import Flask
93 from flask_ldapconn import LDAPConn
94 from ldap3 import SUBTREE
95
96 app = Flask(__name__)
97 ldap = LDAPConn(app)
98
99 @app.route('/')
100 def index():
101 ldapc = ldap.connection
102 basedn = 'ou=people,dc=example,dc=com'
103 search_filter = '(objectClass=posixAccount)'
104 attributes = ['sn', 'givenName', 'uid', 'mail']
105 ldapc.search(basedn, search_filter, SUBTREE,
106 attributes=attributes)
107 response = ldapc.response
108
109
110 User model samples
111 ------------------
112
113 .. code-block:: python
114
115 from flask import Flask
116 from flask_ldapconn import LDAPConn
117
118 app = Flask(__name__)
119 ldap = LDAPConn(app)
120
121 class User(ldap.Entry):
122
123 base_dn = 'ou=people,dc=example,dc=com'
124 object_classes = ['inetOrgPerson']
125
126 name = ldap.Attribute('cn')
127 email = ldap.Attribute('mail')
128 userid = ldap.Attribute('uid')
129 surname = ldap.Attribute('sn')
130 givenname = ldap.Attribute('givenName')
131
132 with app.app_context():
133
134 # get a list of entries
135 entries = User.query.filter('email: *@example.com').all()
136 for entry in entries:
137 print u'Name: {}'.format(entry.name)
138
139 # get the first entry
140 user = User.query.filter('userid: user1').first()
141
142 # new entry
143 new_user = User(
144 name='User Three',
145 email='user3@example.com',
146 userid='user3',
147 surname='Three',
148 givenname='User'
149 )
150 new_user.save()
151
152 # modify entry
153 mod_user = User.query.filter('userid: user1').first()
154 mod_user.name = 'User Number Three'
155 mod_user.email.append.('u.three@example.com')
156 mod_user.givenname.delete()
157 mod_user.save()
158
159 # remove entry
160 rm_user = User.query.filter('userid: user1').first()
161 rm_user.delete()
162
163 # authenticate user
164 auth_user = User.query.filter('userid: user1').first()
165 if auth_user:
166 if auth_user.authenticate('password1234'):
167 print('Authenticated')
168 else:
169 print('Wrong password')
170
171
172 Authenticate with Client
173 ------------------------
174
175 .. code-block:: python
176
177 from flask import Flask
178 from flask_ldapconn import LDAPConn
179
180 app = Flask(__name__)
181 ldap = LDAPConn(app)
182
183 username = 'user1'
184 password = 'userpass'
185 attribute = 'uid'
186 search_filter = ('(active=1)')
187
188 with app.app_context():
189 retval = ldap.authenticate(username, password, attribute,
190 basedn, search_filter)
191 if not retval:
192 return 'Invalid credentials.'
193 return 'Welcome %s.' % username
194
195
196 Bind as user
197 ------------
198
199 To bind as user for the current request instance a new connection from ``flask.g.ldap_conn``:
200
201 .. code-block:: python
202
203 g.ldap_conn = ldap.connect(userdn, password)
204 user = User.query.get(userdn)
205
206
207 Unit Test
208 ---------
209
210 I use a simple Docker image to run the tests on localhost. The test file ``test_flask_ldapconn.py`` tries to handle ``start`` and ``stop`` of the docker container:
211
212 .. code-block:: shell
213
214 pip install docker-py
215 docker pull rroemhild/test-openldap
216 python test_flask_ldapconn.py
217
218 Run the docker container manual:
219
220 .. code-block:: shell
221
222 docker run --privileged -d -p 389:389 --name flask_ldapconn rroemhild/test-openldap
223 DOCKER_RUN=False python test_flask_ldapconn.py
224
225 Unit test with your own settings from a file:
226
227 .. code-block:: shell
228
229 LDAP_SETTINGS=my_settings.py python test_flask_ldapconn.py
230
231
232 Contribute
233 ----------
234
235 #. Check for open issues or open a fresh issue to start a discussion around a feature idea or a bug.
236 #. Fork `the repository`_ on Github to start making your changes.
237 #. Write a test which shows that the bug was fixed or that the feature works as expected.
238 #. Send a pull request and bug the maintainer until it gets merged and published.
239
240 .. _`the repository`: http://github.com/rroemhild/flask-ldapconn
0 Copyright (c) 2017-2018, Rafael Römhild
0 Copyright (c) 2017-2019, Rafael Römhild
11 Copyright (c) 2017, Dominik George <nik@naturalnet.de>
22 All rights reserved.
33
+224
-224
PKG-INFO less more
0 Metadata-Version: 1.1
0 Metadata-Version: 2.1
11 Name: Flask-LDAPConn
2 Version: 0.7.2
2 Version: 0.10.1
33 Summary: Pure python, LDAP connection and ORM for Flask Applications
44 Home-page: http://github.com/rroemhild/flask-ldapconn
55 Author: Rafael Römhild
66 Author-email: rafael@roemhild.de
77 License: BSD
8 Description: Flask-LDAPConn
9 ==============
10
11 .. image:: https://travis-ci.org/rroemhild/flask-ldapconn.svg?branch=master
12 :target: https://travis-ci.org/rroemhild/flask-ldapconn
13
14 .. image:: https://badge.fury.io/py/Flask-LDAPConn.svg
15 :target: https://badge.fury.io/py/Flask-LDAPConn
16
17 Flask-LDAPConn is a Flask extension providing `ldap3 <https://github.com/cannatag/ldap3>`_ (an LDAP V3 pure Python client) connection for accessing LDAP servers.
18
19 To abstract access to LDAP data this extension provides a simple ORM model.
20
21
22 Installation
23 ------------
24
25 .. code-block:: shell
26
27 pip install flask-ldapconn
28
29
30 Configuration
31 -------------
32
33 Your configuration should be declared within your Flask config. Sample configuration:
34
35 .. code-block:: python
36
37 import ssl
38
39 LDAP_SERVER = 'localhost'
40 LDAP_PORT = 389
41 LDAP_BINDDN = 'cn=admin,dc=example,dc=com'
42 LDAP_SECRET = 'forty-two'
43 LDAP_CONNECT_TIMEOUT = 10 # Honored when the TCP connection is being established
44 LDAP_USE_TLS = True # default
45 LDAP_REQUIRE_CERT = ssl.CERT_NONE # default: CERT_REQUIRED
46 LDAP_TLS_VERSION = ssl.PROTOCOL_TLSv1_2 # default: PROTOCOL_TLSv1
47 LDAP_CERT_PATH = '/etc/openldap/certs'
48
49 If you want to always get any entry attribute value as a list, instead of a string if only one item is in the attribute list, then set:
50
51 .. code-block:: python
52
53 FORCE_ATTRIBUTE_VALUE_AS_LIST = True
54
55 Default is ``False`` and will return a string if only one item is in the attribute list.
56
57
58 Setup
59 -----
60
61 Create the LDAP instance in your application.
62
63 .. code-block:: python
64
65 from flask import Flask
66 from flask_ldapconn import LDAPConn
67
68 app = Flask(__name__)
69 ldap = LDAPConn(app)
70
71
72 Client sample
73 -------------
74
75 .. code-block:: python
76
77 from flask import Flask
78 from flask_ldapconn import LDAPConn
79 from ldap3 import SUBTREE
80
81 app = Flask(__name__)
82 ldap = LDAPConn(app)
83
84 @app.route('/')
85 def index():
86 ldapc = ldap.connection
87 basedn = 'ou=people,dc=example,dc=com'
88 search_filter = '(objectClass=posixAccount)'
89 attributes = ['sn', 'givenName', 'uid', 'mail']
90 ldapc.search(basedn, search_filter, SUBTREE,
91 attributes=attributes)
92 response = ldapc.response
93
94
95 User model samples
96 ------------------
97
98 .. code-block:: python
99
100 from flask import Flask
101 from flask_ldapconn import LDAPConn
102
103 app = Flask(__name__)
104 ldap = LDAPConn(app)
105
106 class User(ldap.Entry):
107
108 base_dn = 'ou=people,dc=example,dc=com'
109 object_classes = ['inetOrgPerson']
110
111 name = ldap.Attribute('cn')
112 email = ldap.Attribute('mail')
113 userid = ldap.Attribute('uid')
114 surname = ldap.Attribute('sn')
115 givenname = ldap.Attribute('givenName')
116
117 with app.app_context():
118
119 # get a list of entries
120 entries = User.query.filter('email: *@example.com').all()
121 for entry in entries:
122 print u'Name: {}'.format(entry.name)
123
124 # get the first entry
125 user = User.query.filter('userid: user1').first()
126
127 # new entry
128 new_user = User(
129 name='User Three',
130 email='user3@example.com',
131 userid='user3',
132 surname='Three',
133 givenname='User'
134 )
135 new_user.save()
136
137 # modify entry
138 mod_user = User.query.filter('userid: user1').first()
139 mod_user.name = 'User Number Three'
140 mod_user.email.append.('u.three@example.com')
141 mod_user.givenname.delete()
142 mod_user.save()
143
144 # remove entry
145 rm_user = User.query.filter('userid: user1').first()
146 rm_user.delete()
147
148 # authenticate user
149 auth_user = User.query.filter('userid: user1').first()
150 if auth_user:
151 if auth_user.authenticate('password1234'):
152 print('Authenticated')
153 else:
154 print('Wrong password')
155
156
157 Authenticate with Client
158 ------------------------
159
160 .. code-block:: python
161
162 from flask import Flask
163 from flask_ldapconn import LDAPConn
164
165 app = Flask(__name__)
166 ldap = LDAPConn(app)
167
168 username = 'user1'
169 password = 'userpass'
170 attribute = 'uid'
171 search_filter = ('(active=1)')
172
173 with app.app_context():
174 retval = ldap.authenticate(username, password, attribute,
175 basedn, search_filter)
176 if not retval:
177 return 'Invalid credentials.'
178 return 'Welcome %s.' % username
179
180
181 Bind as user
182 ------------
183
184 To bind as user for the current request instance a new connection from ``flask.g.ldap_conn``:
185
186 .. code-block:: python
187
188 g.ldap_conn = ldap.connect(userdn, password)
189 user = User.query.get(userdn)
190
191
192 Unit Test
193 ---------
194
195 I use a simple Docker image to run the tests on localhost. The test file ``test_flask_ldapconn.py`` tries to handle ``start`` and ``stop`` of the docker container:
196
197 .. code-block:: shell
198
199 pip install docker-py
200 docker pull rroemhild/test-openldap
201 python test_flask_ldapconn.py
202
203 Run the docker container manual:
204
205 .. code-block:: shell
206
207 docker run --privileged -d -p 389:389 --name flask_ldapconn rroemhild/test-openldap
208 DOCKER_RUN=False python test_flask_ldapconn.py
209
210 Unit test with your own settings from a file:
211
212 .. code-block:: shell
213
214 LDAP_SETTINGS=my_settings.py python test_flask_ldapconn.py
215
216
217 Contribute
218 ----------
219
220 #. Check for open issues or open a fresh issue to start a discussion around a feature idea or a bug.
221 #. Fork `the repository`_ on Github to start making your changes.
222 #. Write a test which shows that the bug was fixed or that the feature works as expected.
223 #. Send a pull request and bug the maintainer until it gets merged and published.
224
225 .. _`the repository`: http://github.com/rroemhild/flask-ldapconn
226
2278 Keywords: flask ldap ldap3 orm
2289 Platform: any
229 Classifier: Development Status :: 4 - Beta
10 Classifier: Development Status :: 5 - Production/Stable
23011 Classifier: Environment :: Web Environment
23112 Classifier: Intended Audience :: Developers
23213 Classifier: License :: OSI Approved :: BSD License
23314 Classifier: Operating System :: OS Independent
234 Classifier: Programming Language :: Python :: 2.7
235 Classifier: Programming Language :: Python :: 3.4
23615 Classifier: Programming Language :: Python :: 3.5
23716 Classifier: Programming Language :: Python :: 3.6
17 Classifier: Programming Language :: Python :: 3.7
23818 Classifier: Framework :: Flask
23919 Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
24020 Classifier: Topic :: Software Development :: Libraries :: Python Modules
21 License-File: LICENSE
22
23 Flask-LDAPConn
24 ==============
25
26 .. image:: https://travis-ci.org/rroemhild/flask-ldapconn.svg?branch=master
27 :target: https://travis-ci.org/rroemhild/flask-ldapconn
28
29 .. image:: https://badge.fury.io/py/Flask-LDAPConn.svg
30 :target: https://badge.fury.io/py/Flask-LDAPConn
31
32 Flask-LDAPConn is a Flask extension providing `ldap3 <https://github.com/cannatag/ldap3>`_ (an LDAP V3 pure Python client) connection for accessing LDAP servers.
33
34 To abstract access to LDAP data this extension provides a simple ORM model.
35
36
37 Installation
38 ------------
39
40 .. code-block:: shell
41
42 pip install flask-ldapconn
43
44
45 Configuration
46 -------------
47
48 Your configuration should be declared within your Flask config. Sample configuration:
49
50 .. code-block:: python
51
52 import ssl
53
54 LDAP_SERVER = 'localhost'
55 LDAP_PORT = 389
56 LDAP_BINDDN = 'cn=admin,dc=example,dc=com'
57 LDAP_SECRET = 'forty-two'
58 LDAP_CONNECT_TIMEOUT = 10 # Honored when the TCP connection is being established
59 LDAP_USE_TLS = True # default
60 LDAP_REQUIRE_CERT = ssl.CERT_NONE # default: CERT_REQUIRED
61 LDAP_TLS_VERSION = ssl.PROTOCOL_TLSv1_2 # default: PROTOCOL_TLSv1
62 LDAP_CERT_PATH = '/etc/openldap/certs'
63
64 If you want to always get any entry attribute value as a list, instead of a string if only one item is in the attribute list, then set:
65
66 .. code-block:: python
67
68 FORCE_ATTRIBUTE_VALUE_AS_LIST = True
69
70 Default is ``False`` and will return a string if only one item is in the attribute list.
71
72
73 Setup
74 -----
75
76 Create the LDAP instance in your application.
77
78 .. code-block:: python
79
80 from flask import Flask
81 from flask_ldapconn import LDAPConn
82
83 app = Flask(__name__)
84 ldap = LDAPConn(app)
85
86
87 Client sample
88 -------------
89
90 .. code-block:: python
91
92 from flask import Flask
93 from flask_ldapconn import LDAPConn
94 from ldap3 import SUBTREE
95
96 app = Flask(__name__)
97 ldap = LDAPConn(app)
98
99 @app.route('/')
100 def index():
101 ldapc = ldap.connection
102 basedn = 'ou=people,dc=example,dc=com'
103 search_filter = '(objectClass=posixAccount)'
104 attributes = ['sn', 'givenName', 'uid', 'mail']
105 ldapc.search(basedn, search_filter, SUBTREE,
106 attributes=attributes)
107 response = ldapc.response
108
109
110 User model samples
111 ------------------
112
113 .. code-block:: python
114
115 from flask import Flask
116 from flask_ldapconn import LDAPConn
117
118 app = Flask(__name__)
119 ldap = LDAPConn(app)
120
121 class User(ldap.Entry):
122
123 base_dn = 'ou=people,dc=example,dc=com'
124 object_classes = ['inetOrgPerson']
125
126 name = ldap.Attribute('cn')
127 email = ldap.Attribute('mail')
128 userid = ldap.Attribute('uid')
129 surname = ldap.Attribute('sn')
130 givenname = ldap.Attribute('givenName')
131
132 with app.app_context():
133
134 # get a list of entries
135 entries = User.query.filter('email: *@example.com').all()
136 for entry in entries:
137 print u'Name: {}'.format(entry.name)
138
139 # get the first entry
140 user = User.query.filter('userid: user1').first()
141
142 # new entry
143 new_user = User(
144 name='User Three',
145 email='user3@example.com',
146 userid='user3',
147 surname='Three',
148 givenname='User'
149 )
150 new_user.save()
151
152 # modify entry
153 mod_user = User.query.filter('userid: user1').first()
154 mod_user.name = 'User Number Three'
155 mod_user.email.append.('u.three@example.com')
156 mod_user.givenname.delete()
157 mod_user.save()
158
159 # remove entry
160 rm_user = User.query.filter('userid: user1').first()
161 rm_user.delete()
162
163 # authenticate user
164 auth_user = User.query.filter('userid: user1').first()
165 if auth_user:
166 if auth_user.authenticate('password1234'):
167 print('Authenticated')
168 else:
169 print('Wrong password')
170
171
172 Authenticate with Client
173 ------------------------
174
175 .. code-block:: python
176
177 from flask import Flask
178 from flask_ldapconn import LDAPConn
179
180 app = Flask(__name__)
181 ldap = LDAPConn(app)
182
183 username = 'user1'
184 password = 'userpass'
185 attribute = 'uid'
186 search_filter = ('(active=1)')
187
188 with app.app_context():
189 retval = ldap.authenticate(username, password, attribute,
190 basedn, search_filter)
191 if not retval:
192 return 'Invalid credentials.'
193 return 'Welcome %s.' % username
194
195
196 Bind as user
197 ------------
198
199 To bind as user for the current request instance a new connection from ``flask.g.ldap_conn``:
200
201 .. code-block:: python
202
203 g.ldap_conn = ldap.connect(userdn, password)
204 user = User.query.get(userdn)
205
206
207 Unit Test
208 ---------
209
210 I use a simple Docker image to run the tests on localhost. The test file ``test_flask_ldapconn.py`` tries to handle ``start`` and ``stop`` of the docker container:
211
212 .. code-block:: shell
213
214 pip install docker-py
215 docker pull rroemhild/test-openldap
216 python test_flask_ldapconn.py
217
218 Run the docker container manual:
219
220 .. code-block:: shell
221
222 docker run --privileged -d -p 389:389 --name flask_ldapconn rroemhild/test-openldap
223 DOCKER_RUN=False python test_flask_ldapconn.py
224
225 Unit test with your own settings from a file:
226
227 .. code-block:: shell
228
229 LDAP_SETTINGS=my_settings.py python test_flask_ldapconn.py
230
231
232 Contribute
233 ----------
234
235 #. Check for open issues or open a fresh issue to start a discussion around a feature idea or a bug.
236 #. Fork `the repository`_ on Github to start making your changes.
237 #. Write a test which shows that the bug was fixed or that the feature works as expected.
238 #. Send a pull request and bug the maintainer until it gets merged and published.
239
240 .. _`the repository`: http://github.com/rroemhild/flask-ldapconn
33 from flask import current_app, _app_ctx_stack
44 from ldap3 import Server, Connection, Tls
55 from ldap3 import SYNC, ALL, SUBTREE
6 from ldap3 import AUTO_BIND_NO_TLS, AUTO_BIND_TLS_BEFORE_BIND
6 from ldap3 import AUTO_BIND_NONE, AUTO_BIND_NO_TLS, AUTO_BIND_TLS_BEFORE_BIND
7 from ldap3 import ANONYMOUS, SIMPLE, SASL
78 from ldap3.core.exceptions import (LDAPBindError, LDAPInvalidFilterError,
89 LDAPInvalidDnError)
910 from ldap3.utils.dn import parse_dn
1011
1112 from .entry import LDAPEntry
12 from .attribute import LDAPAttribute
13 from .attribute import LdapField
1314
1415
1516 __all__ = ('LDAPConn',)
2021 def __init__(self, app=None):
2122
2223 self.Entry = LDAPEntry
23 self.Attribute = LDAPAttribute
24 self.Attribute = LdapField
2425 self.Model = self.Entry
2526 self.app = app
2627
8485 # Teardown appcontext
8586 app.teardown_appcontext(self.teardown)
8687
87 def connect(self, user, password):
88 def connect(self, user, password, anonymous=False):
8889 auto_bind_strategy = AUTO_BIND_TLS_BEFORE_BIND
90 authentication_policy = SIMPLE
8991 if current_app.config['LDAP_USE_TLS'] is not True:
9092 auto_bind_strategy = AUTO_BIND_NO_TLS
93 if anonymous:
94 authentication_policy = ANONYMOUS
95 user = None
96 password = None
9197
9298 ldap_conn = Connection(
9399 self.ldap_server,
94100 auto_bind=auto_bind_strategy,
95101 client_strategy=current_app.config['LDAP_CONNECTION_STRATEGY'],
96102 raise_exceptions=current_app.config['LDAP_RAISE_EXCEPTIONS'],
103 authentication=authentication_policy,
97104 user=user,
98105 password=password,
99106 check_names=True,
114121 if not hasattr(ctx, 'ldap_conn'):
115122 ctx.ldap_conn = self.connect(
116123 current_app.config['LDAP_BINDDN'],
117 current_app.config['LDAP_SECRET']
124 current_app.config['LDAP_SECRET'],
125 anonymous=None in [current_app.config['LDAP_BINDDN'], current_app.config['LDAP_SECRET']]
118126 )
119127 return ctx.ldap_conn
120128
77 MODIFY_REPLACE)
88
99
10 class LdapField(object):
11
12 def __init__(self, name, validate=None, default=None, dereference_dn=None):
13 self.name = name
14 self.validate = validate
15 self.default = default
16 self.dereference_dn = None
17
18 def get_abstract_attr_def(self, key):
19 return AttrDef(name=self.name, key=key,
20 validate=self.validate,
21 default=self.default,
22 dereference_dn=self.dereference_dn)
23
24
1025 class LDAPAttribute(object):
1126
12 def __init__(self, name, validate=None, default=None, dereference_dn=None):
27 def __init__(self, name):
1328 self.__dict__['name'] = name
1429 self.__dict__['values'] = []
15 self.__dict__['default'] = default
16 self.__dict__['validate'] = validate
1730 self.__dict__['changetype'] = None
18 self.__dict__['dereference_dn'] = dereference_dn
1931
2032 def __str__(self):
2133 if isinstance(self.value, STRING_TYPES):
8496 to delete.
8597 '''
8698 self.value = []
87
88 def get_abstract_attr_def(self, key):
89 return AttrDef(name=self.name, key=key,
90 validate=self.validate,
91 default=self.default,
92 dereference_dn=self.dereference_dn)
00 # -*- coding: utf-8 -*-
11 import json
2
2 from flask import current_app
33 from six import add_metaclass
4 from copy import deepcopy
5 from importlib import import_module
6
7 from flask import current_app
8 from ldap3 import ObjectDef
9 from ldap3.core.exceptions import LDAPAttributeError
104 from ldap3.utils.dn import safe_dn
115 from ldap3.utils.conv import check_json_dict, format_json
6 from ldap3.core.exceptions import LDAPAttributeError
127
138 from .query import BaseQuery
14 from .attribute import LDAPAttribute
9 from .attribute import LDAPAttribute, LdapField
1510
1611
1712 __all__ = ('LDAPEntry',)
1914
2015 class LDAPEntryMeta(type):
2116
22 # requiered
23 base_dn = None
24 entry_rdn = ['cn']
25 object_classes = ['top']
17 def __init__(cls, name, bases, attr):
18 cls._fields = {}
19 for key, value in attr.items():
20 if isinstance(value, LdapField):
21 cls._fields[key] = value
2622
27 # optional
28 sub_tree = True
29 operational_attributes = False
30
31 def __init__(cls, name, bases, ns):
32 cls._attributes = dict()
33
34 # Merge attributes and object classes from parents
3523 for base in bases:
3624 if isinstance(base, LDAPEntryMeta):
37 cls._attributes.update(base._attributes)
25 cls._fields.update(base._fields)
3826 # Deduplicate object classes
3927 cls.object_classes = list(
4028 set(cls.object_classes + base.object_classes))
41
42 # Create object definition
43 cls._object_def = ObjectDef(cls.object_classes)
44
45 # loop through the namespace looking for LDAPAttribute instances
46 for key, value in ns.items():
47 if isinstance(value, LDAPAttribute):
48 cls._attributes[key] = value
49
50 # Generate attribute definitions
51 for key in cls._attributes:
52 attr_def = cls._attributes[key].get_abstract_attr_def(key)
53 cls._object_def.add_attribute(attr_def)
5429
5530 @property
5631 def query(cls):
5732 return BaseQuery(cls)
5833
59 def get_new_type(cls):
60 class_dict = deepcopy(cls()._attributes)
61 module = import_module(cls.__module__)
62 obj = getattr(module, cls.__name__)
63 new_cls = type(cls.__name__, (obj,), class_dict)
64 return new_cls
65
6634
6735 @add_metaclass(LDAPEntryMeta)
6836 class LDAPEntry(object):
6937
38 base_dn = None
39 entry_rdn = ['cn']
40 object_classes = ['top']
41 sub_tree = True
42 operational_attributes = False
43 _changetype = 'add'
44
7045 def __init__(self, dn=None, changetype='add', **kwargs):
71 self.__dict__['_dn'] = dn
72 self.__dict__['_changetype'] = changetype
73
74 for key, value in kwargs.items():
75 if key not in self._attributes:
76 raise LDAPAttributeError('attribute not found')
77 self._attributes[key]._init = value
78
79 def __iter__(self):
80 for attribute in self._attributes:
81 yield self._attributes[attribute]
82
83 def __contains__(self, item):
84 return item in self._attributes
85
86 def __getitem__(self, item):
87 if item in self.__attributes:
88 return self.__getattr__(item)
89 else:
90 raise KeyError(item)
91
92 def __getattribute__(self, item):
93 if item != '_attributes' and item in self._attributes:
94 return self._attributes[item].value
95 else:
96 return object.__getattribute__(self, item)
97
98 def __setitem__(self, key, value):
99 if key in self._attributes:
100 self.__setattr__(key, value)
101 else:
102 raise KeyError(key)
103
104 def __setattr__(self, key, value):
105 if key in self._attributes:
106 self._attributes[key].value = value
107 else:
108 return object.__setattr__(self, key, value)
46 self._attributes = {}
47 self._dn = dn
48 self._changetype = changetype
49 if kwargs:
50 for key, value in kwargs.items():
51 self._store_attr(key, value, init=True)
52 for key, ldap_attr in self._fields.items():
53 if not self._isstored(key):
54 self._store_attr(key, [])
10955
11056 @property
11157 def dn(self):
11561
11662 def generate_dn_from_entry(self):
11763 rdn_list = list()
118 for attr in self._object_def:
64 for key, attr in self._attributes.items():
11965 if attr.name in self.entry_rdn:
120 if len(self._attributes[attr.key]) == 1:
66 if len(self._attributes[key]) == 1:
12167 rdn = '{attr}={value}'.format(
12268 attr=attr.name,
123 value=self._attributes[attr.key].value
69 value=self._attributes[key].value
12470 )
12571 rdn_list.append(rdn)
126
12772 dn = '{rdn},{base_dn}'.format(rdn='+'.join(rdn_list),
12873 base_dn=self.base_dn)
74 self._dn = safe_dn(dn)
12975
130 self.__dict__['_dn'] = safe_dn(dn)
76 @classmethod
77 def _get_field(cls, attr):
78 return cls._fields.get(attr)
79
80 @classmethod
81 def _get_field_name(cls, attr):
82 if cls._get_field(attr):
83 return cls._get_field(attr).name
84
85 def _store_attr(self, attr, value=[], init=False):
86 if not self._get_field(attr):
87 raise LDAPAttributeError('attribute not found')
88 if value is None:
89 value = []
90 if not self._attributes.get(attr):
91 self._attributes[attr] = LDAPAttribute(self._get_field_name(attr))
92 self._attributes[attr].value = value
93 if init:
94 self._attributes[attr].__dict__['changetype'] = None
95
96 def _isstored(self, attr):
97 return self._attributes.get(attr)
98
99 def _get_attr(self, attr):
100 if self._isstored(attr):
101 return self._attributes[attr].value
102 return None
103
104 def __getattribute__(self, item):
105 if item != '_fields' and item in self._fields:
106 return self._get_attr(item)
107 return super(LDAPModel, self).__getattribute__(item)
108
109 def __setattr__(self, key, value):
110 if key != '_fields' and key in self._fields:
111 self._store_attr(key, value)
112 else:
113 return super(LDAPModel, self).__setattr__(key, value)
131114
132115 def get_attributes_dict(self):
133116 return dict((attribute_key, attribute_value.values) for (attribute_key,
136119 def get_entry_add_dict(self, attr_dict):
137120 add_dict = dict()
138121 for attribute_key, attribute_value in attr_dict.items():
139 if self._attributes[attribute_key].value:
140 attribute_def = self._object_def[attribute_key]
141 add_dict.update({attribute_def.name: attribute_value})
122 if self._attributes[attribute_key].value != []:
123 add_dict.update({self._get_field_name(attribute_key): attribute_value})
142124 return add_dict
143125
144126 def get_entry_modify_dict(self, attr_dict):
145127 modify_dict = dict()
146128 for attribute_key in attr_dict.keys():
147129 if self._attributes[attribute_key].changetype is not None:
148 attribute_def = self._object_def[attribute_key]
149130 changes = self._attributes[attribute_key].get_changes_tuple()
150 modify_dict.update({attribute_def.name: changes})
131 modify_dict.update({self._get_field_name(attribute_key): changes})
151132 return modify_dict
152133
153134 @property
156137
157138 def delete(self):
158139 '''Delete this entry from LDAP server'''
159 self.connection.connection.delete(self.dn)
140 return self.connection.connection.delete(self.dn)
160141
161142 def save(self):
162143 '''Save the current instance'''
163 attributes = self.get_entry_add_dict(self.get_attributes_dict())
144 attrs = self.get_attributes_dict()
164145 if self._changetype == 'add':
146 changes = self.get_entry_add_dict(attrs)
165147 return self.connection.connection.add(self.dn,
166148 self.object_classes,
167 attributes)
149 changes)
168150 elif self._changetype == 'modify':
169 changes = self.get_entry_modify_dict(self.get_attributes_dict())
151 changes = self.get_entry_modify_dict(attrs)
170152 return self.connection.connection.modify(self.dn, changes)
171153
172154 return False
00 # -*- coding: utf-8 -*-
1 import sys
12 from flask import current_app
2 from ldap3 import BASE, Reader
3 from ldap3 import BASE, Reader, SUBTREE, ObjectDef
34
45
56 __all__ = ('BaseQuery',)
78
89 class BaseQuery(object):
910
10 def __init__(self, type):
11 self.type = type
11 def __init__(self, obj):
12 self.obj = obj
13 self.query = []
14 self.base_dn = obj.base_dn
15 self.sub_tree = obj.sub_tree
16 self.object_def = ObjectDef(obj.object_classes)
17 self.operational_attributes = obj.operational_attributes
18 self.components_in_and = True
1219
13 self.query = []
14 self.base_dn = getattr(type, 'base_dn')
15 self.sub_tree = getattr(type, 'sub_tree')
16 self.object_def = getattr(type, '_object_def')
17 self.components_in_and = True
18 self.operational_attributes = getattr(type, 'operational_attributes')
20 def add_abstract_attr_def(self):
21 for name, attr in self.obj._fields.items():
22 attr_def = attr.get_abstract_attr_def(name)
23 self.object_def.add_attribute(attr_def)
1924
2025 def __iter__(self):
2126 for entry in self.get_reader_result():
22 new_cls = self.type.get_new_type()
27 module = sys.modules.get(self.obj.__module__)
28 new_cls = getattr(module, self.obj.__name__)
2329 ldapentry = new_cls(dn=entry.entry_dn,
2430 changetype='modify',
2531 **entry.entry_attributes_as_dict)
2834 def get_reader_result(self):
2935 query = ','.join(self.query)
3036 ldapc = current_app.extensions.get('ldap_conn')
37 self.add_abstract_attr_def()
3138 reader = Reader(connection=ldapc.connection,
3239 object_def=self.object_def,
3340 query=query,
1313
1414 setup(
1515 name='Flask-LDAPConn',
16 version='0.7.2',
16 version='0.10.1',
1717 url='http://github.com/rroemhild/flask-ldapconn',
1818 license='BSD',
1919 author='Rafael Römhild',
3131 'six>=1.10'
3232 ],
3333 classifiers=[
34 'Development Status :: 4 - Beta',
34 'Development Status :: 5 - Production/Stable',
3535 'Environment :: Web Environment',
3636 'Intended Audience :: Developers',
3737 'License :: OSI Approved :: BSD License',
3838 'Operating System :: OS Independent',
39 'Programming Language :: Python :: 2.7',
40 'Programming Language :: Python :: 3.4',
4139 'Programming Language :: Python :: 3.5',
4240 'Programming Language :: Python :: 3.6',
41 'Programming Language :: Python :: 3.7',
4342 'Framework :: Flask',
4443 'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
4544 'Topic :: Software Development :: Libraries :: Python Modules'