Import upstream version 0.10.1+git20210602.1.46bf8ed
Debian Janitor
1 year, 4 months ago
0 | 0 | Changelog |
1 | 1 | ========= |
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 | |
2 | 26 | |
3 | 27 | 0.7.2 (2018-06-14) |
4 | 28 | ------------------ |
0 | Metadata-Version: 1.1 | |
0 | Metadata-Version: 2.1 | |
1 | 1 | Name: Flask-LDAPConn |
2 | Version: 0.7.2 | |
2 | Version: 0.10.1 | |
3 | 3 | Summary: Pure python, LDAP connection and ORM for Flask Applications |
4 | 4 | Home-page: http://github.com/rroemhild/flask-ldapconn |
5 | 5 | Author: Rafael Römhild |
6 | 6 | Author-email: rafael@roemhild.de |
7 | 7 | 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 | ||
227 | 8 | Keywords: flask ldap ldap3 orm |
228 | 9 | Platform: any |
229 | Classifier: Development Status :: 4 - Beta | |
10 | Classifier: Development Status :: 5 - Production/Stable | |
230 | 11 | Classifier: Environment :: Web Environment |
231 | 12 | Classifier: Intended Audience :: Developers |
232 | 13 | Classifier: License :: OSI Approved :: BSD License |
233 | 14 | Classifier: Operating System :: OS Independent |
234 | Classifier: Programming Language :: Python :: 2.7 | |
235 | Classifier: Programming Language :: Python :: 3.4 | |
236 | 15 | Classifier: Programming Language :: Python :: 3.5 |
237 | 16 | Classifier: Programming Language :: Python :: 3.6 |
17 | Classifier: Programming Language :: Python :: 3.7 | |
238 | 18 | Classifier: Framework :: Flask |
239 | 19 | Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content |
240 | 20 | 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 | |
1 | 1 | Copyright (c) 2017, Dominik George <nik@naturalnet.de> |
2 | 2 | All rights reserved. |
3 | 3 |
0 | Metadata-Version: 1.1 | |
0 | Metadata-Version: 2.1 | |
1 | 1 | Name: Flask-LDAPConn |
2 | Version: 0.7.2 | |
2 | Version: 0.10.1 | |
3 | 3 | Summary: Pure python, LDAP connection and ORM for Flask Applications |
4 | 4 | Home-page: http://github.com/rroemhild/flask-ldapconn |
5 | 5 | Author: Rafael Römhild |
6 | 6 | Author-email: rafael@roemhild.de |
7 | 7 | 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 | ||
227 | 8 | Keywords: flask ldap ldap3 orm |
228 | 9 | Platform: any |
229 | Classifier: Development Status :: 4 - Beta | |
10 | Classifier: Development Status :: 5 - Production/Stable | |
230 | 11 | Classifier: Environment :: Web Environment |
231 | 12 | Classifier: Intended Audience :: Developers |
232 | 13 | Classifier: License :: OSI Approved :: BSD License |
233 | 14 | Classifier: Operating System :: OS Independent |
234 | Classifier: Programming Language :: Python :: 2.7 | |
235 | Classifier: Programming Language :: Python :: 3.4 | |
236 | 15 | Classifier: Programming Language :: Python :: 3.5 |
237 | 16 | Classifier: Programming Language :: Python :: 3.6 |
17 | Classifier: Programming Language :: Python :: 3.7 | |
238 | 18 | Classifier: Framework :: Flask |
239 | 19 | Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content |
240 | 20 | 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 |
3 | 3 | from flask import current_app, _app_ctx_stack |
4 | 4 | from ldap3 import Server, Connection, Tls |
5 | 5 | 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 | |
7 | 8 | from ldap3.core.exceptions import (LDAPBindError, LDAPInvalidFilterError, |
8 | 9 | LDAPInvalidDnError) |
9 | 10 | from ldap3.utils.dn import parse_dn |
10 | 11 | |
11 | 12 | from .entry import LDAPEntry |
12 | from .attribute import LDAPAttribute | |
13 | from .attribute import LdapField | |
13 | 14 | |
14 | 15 | |
15 | 16 | __all__ = ('LDAPConn',) |
20 | 21 | def __init__(self, app=None): |
21 | 22 | |
22 | 23 | self.Entry = LDAPEntry |
23 | self.Attribute = LDAPAttribute | |
24 | self.Attribute = LdapField | |
24 | 25 | self.Model = self.Entry |
25 | 26 | self.app = app |
26 | 27 | |
84 | 85 | # Teardown appcontext |
85 | 86 | app.teardown_appcontext(self.teardown) |
86 | 87 | |
87 | def connect(self, user, password): | |
88 | def connect(self, user, password, anonymous=False): | |
88 | 89 | auto_bind_strategy = AUTO_BIND_TLS_BEFORE_BIND |
90 | authentication_policy = SIMPLE | |
89 | 91 | if current_app.config['LDAP_USE_TLS'] is not True: |
90 | 92 | auto_bind_strategy = AUTO_BIND_NO_TLS |
93 | if anonymous: | |
94 | authentication_policy = ANONYMOUS | |
95 | user = None | |
96 | password = None | |
91 | 97 | |
92 | 98 | ldap_conn = Connection( |
93 | 99 | self.ldap_server, |
94 | 100 | auto_bind=auto_bind_strategy, |
95 | 101 | client_strategy=current_app.config['LDAP_CONNECTION_STRATEGY'], |
96 | 102 | raise_exceptions=current_app.config['LDAP_RAISE_EXCEPTIONS'], |
103 | authentication=authentication_policy, | |
97 | 104 | user=user, |
98 | 105 | password=password, |
99 | 106 | check_names=True, |
114 | 121 | if not hasattr(ctx, 'ldap_conn'): |
115 | 122 | ctx.ldap_conn = self.connect( |
116 | 123 | 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']] | |
118 | 126 | ) |
119 | 127 | return ctx.ldap_conn |
120 | 128 |
7 | 7 | MODIFY_REPLACE) |
8 | 8 | |
9 | 9 | |
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 | ||
10 | 25 | class LDAPAttribute(object): |
11 | 26 | |
12 | def __init__(self, name, validate=None, default=None, dereference_dn=None): | |
27 | def __init__(self, name): | |
13 | 28 | self.__dict__['name'] = name |
14 | 29 | self.__dict__['values'] = [] |
15 | self.__dict__['default'] = default | |
16 | self.__dict__['validate'] = validate | |
17 | 30 | self.__dict__['changetype'] = None |
18 | self.__dict__['dereference_dn'] = dereference_dn | |
19 | 31 | |
20 | 32 | def __str__(self): |
21 | 33 | if isinstance(self.value, STRING_TYPES): |
84 | 96 | to delete. |
85 | 97 | ''' |
86 | 98 | 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) |
0 | 0 | # -*- coding: utf-8 -*- |
1 | 1 | import json |
2 | ||
2 | from flask import current_app | |
3 | 3 | 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 | |
10 | 4 | from ldap3.utils.dn import safe_dn |
11 | 5 | from ldap3.utils.conv import check_json_dict, format_json |
6 | from ldap3.core.exceptions import LDAPAttributeError | |
12 | 7 | |
13 | 8 | from .query import BaseQuery |
14 | from .attribute import LDAPAttribute | |
9 | from .attribute import LDAPAttribute, LdapField | |
15 | 10 | |
16 | 11 | |
17 | 12 | __all__ = ('LDAPEntry',) |
19 | 14 | |
20 | 15 | class LDAPEntryMeta(type): |
21 | 16 | |
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 | |
26 | 22 | |
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 | |
35 | 23 | for base in bases: |
36 | 24 | if isinstance(base, LDAPEntryMeta): |
37 | cls._attributes.update(base._attributes) | |
25 | cls._fields.update(base._fields) | |
38 | 26 | # Deduplicate object classes |
39 | 27 | cls.object_classes = list( |
40 | 28 | 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) | |
54 | 29 | |
55 | 30 | @property |
56 | 31 | def query(cls): |
57 | 32 | return BaseQuery(cls) |
58 | 33 | |
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 | ||
66 | 34 | |
67 | 35 | @add_metaclass(LDAPEntryMeta) |
68 | 36 | class LDAPEntry(object): |
69 | 37 | |
38 | base_dn = None | |
39 | entry_rdn = ['cn'] | |
40 | object_classes = ['top'] | |
41 | sub_tree = True | |
42 | operational_attributes = False | |
43 | _changetype = 'add' | |
44 | ||
70 | 45 | 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, []) | |
109 | 55 | |
110 | 56 | @property |
111 | 57 | def dn(self): |
115 | 61 | |
116 | 62 | def generate_dn_from_entry(self): |
117 | 63 | rdn_list = list() |
118 | for attr in self._object_def: | |
64 | for key, attr in self._attributes.items(): | |
119 | 65 | if attr.name in self.entry_rdn: |
120 | if len(self._attributes[attr.key]) == 1: | |
66 | if len(self._attributes[key]) == 1: | |
121 | 67 | rdn = '{attr}={value}'.format( |
122 | 68 | attr=attr.name, |
123 | value=self._attributes[attr.key].value | |
69 | value=self._attributes[key].value | |
124 | 70 | ) |
125 | 71 | rdn_list.append(rdn) |
126 | ||
127 | 72 | dn = '{rdn},{base_dn}'.format(rdn='+'.join(rdn_list), |
128 | 73 | base_dn=self.base_dn) |
74 | self._dn = safe_dn(dn) | |
129 | 75 | |
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) | |
131 | 114 | |
132 | 115 | def get_attributes_dict(self): |
133 | 116 | return dict((attribute_key, attribute_value.values) for (attribute_key, |
136 | 119 | def get_entry_add_dict(self, attr_dict): |
137 | 120 | add_dict = dict() |
138 | 121 | 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}) | |
142 | 124 | return add_dict |
143 | 125 | |
144 | 126 | def get_entry_modify_dict(self, attr_dict): |
145 | 127 | modify_dict = dict() |
146 | 128 | for attribute_key in attr_dict.keys(): |
147 | 129 | if self._attributes[attribute_key].changetype is not None: |
148 | attribute_def = self._object_def[attribute_key] | |
149 | 130 | 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}) | |
151 | 132 | return modify_dict |
152 | 133 | |
153 | 134 | @property |
156 | 137 | |
157 | 138 | def delete(self): |
158 | 139 | '''Delete this entry from LDAP server''' |
159 | self.connection.connection.delete(self.dn) | |
140 | return self.connection.connection.delete(self.dn) | |
160 | 141 | |
161 | 142 | def save(self): |
162 | 143 | '''Save the current instance''' |
163 | attributes = self.get_entry_add_dict(self.get_attributes_dict()) | |
144 | attrs = self.get_attributes_dict() | |
164 | 145 | if self._changetype == 'add': |
146 | changes = self.get_entry_add_dict(attrs) | |
165 | 147 | return self.connection.connection.add(self.dn, |
166 | 148 | self.object_classes, |
167 | attributes) | |
149 | changes) | |
168 | 150 | elif self._changetype == 'modify': |
169 | changes = self.get_entry_modify_dict(self.get_attributes_dict()) | |
151 | changes = self.get_entry_modify_dict(attrs) | |
170 | 152 | return self.connection.connection.modify(self.dn, changes) |
171 | 153 | |
172 | 154 | return False |
0 | 0 | # -*- coding: utf-8 -*- |
1 | import sys | |
1 | 2 | from flask import current_app |
2 | from ldap3 import BASE, Reader | |
3 | from ldap3 import BASE, Reader, SUBTREE, ObjectDef | |
3 | 4 | |
4 | 5 | |
5 | 6 | __all__ = ('BaseQuery',) |
7 | 8 | |
8 | 9 | class BaseQuery(object): |
9 | 10 | |
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 | |
12 | 19 | |
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) | |
19 | 24 | |
20 | 25 | def __iter__(self): |
21 | 26 | 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__) | |
23 | 29 | ldapentry = new_cls(dn=entry.entry_dn, |
24 | 30 | changetype='modify', |
25 | 31 | **entry.entry_attributes_as_dict) |
28 | 34 | def get_reader_result(self): |
29 | 35 | query = ','.join(self.query) |
30 | 36 | ldapc = current_app.extensions.get('ldap_conn') |
37 | self.add_abstract_attr_def() | |
31 | 38 | reader = Reader(connection=ldapc.connection, |
32 | 39 | object_def=self.object_def, |
33 | 40 | query=query, |
13 | 13 | |
14 | 14 | setup( |
15 | 15 | name='Flask-LDAPConn', |
16 | version='0.7.2', | |
16 | version='0.10.1', | |
17 | 17 | url='http://github.com/rroemhild/flask-ldapconn', |
18 | 18 | license='BSD', |
19 | 19 | author='Rafael Römhild', |
31 | 31 | 'six>=1.10' |
32 | 32 | ], |
33 | 33 | classifiers=[ |
34 | 'Development Status :: 4 - Beta', | |
34 | 'Development Status :: 5 - Production/Stable', | |
35 | 35 | 'Environment :: Web Environment', |
36 | 36 | 'Intended Audience :: Developers', |
37 | 37 | 'License :: OSI Approved :: BSD License', |
38 | 38 | 'Operating System :: OS Independent', |
39 | 'Programming Language :: Python :: 2.7', | |
40 | 'Programming Language :: Python :: 3.4', | |
41 | 39 | 'Programming Language :: Python :: 3.5', |
42 | 40 | 'Programming Language :: Python :: 3.6', |
41 | 'Programming Language :: Python :: 3.7', | |
43 | 42 | 'Framework :: Flask', |
44 | 43 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', |
45 | 44 | 'Topic :: Software Development :: Libraries :: Python Modules' |