0001-ldap-do-not-block-check_password-and-get_attributes-.patch
src/authentic2/attributes_ng/sources/ldap.py | ||
---|---|---|
38 | 38 |
def get_attributes(instance, ctx): |
39 | 39 |
user = ctx.get('user') |
40 | 40 |
if user and isinstance(user, LDAPUser): |
41 |
ctx.update(user.get_attributes()) |
|
41 |
ctx.update(user.get_attributes(instance, ctx))
|
|
42 | 42 |
return ctx |
src/authentic2/backends/ldap_backend.py | ||
---|---|---|
14 | 14 |
# You should have received a copy of the GNU Affero General Public License |
15 | 15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 |
import hashlib |
|
18 | ||
17 | 19 |
try: |
18 | 20 |
import ldap |
19 | 21 |
import ldap.modlist |
... | ... | |
35 | 37 |
# code originaly copied from by now merely inspired by |
36 | 38 |
# http://www.amherst.k12.oh.us/django-ldap.html |
37 | 39 | |
40 |
from django.core.cache import cache |
|
38 | 41 |
from django.core.exceptions import ImproperlyConfigured |
39 | 42 |
from django.conf import settings |
40 | 43 |
from django.contrib.auth import get_user_model |
... | ... | |
70 | 73 |
'/var/lib/ca-certificates/ca-bundle.pem', # OpenSuse |
71 | 74 |
] |
72 | 75 | |
76 | ||
73 | 77 |
# Select a system certificate store |
74 | 78 |
for bundle_path in CA_BUNDLE_PATHS: |
75 | 79 |
if os.path.exists(bundle_path): |
... | ... | |
327 | 331 | |
328 | 332 |
def check_password(self, raw_password): |
329 | 333 |
connection = self.ldap_backend.get_connection(self.block) |
330 |
try: |
|
331 |
connection.simple_bind_s(self.dn, raw_password) |
|
332 |
except ldap.INVALID_CREDENTIALS: |
|
333 |
return False |
|
334 |
except ldap.LDAPError as e: |
|
335 |
log.error('LDAPUser.check_password() failed: %r', e) |
|
336 |
return False |
|
337 |
self._current_password = raw_password |
|
338 |
return True |
|
334 |
if connection: |
|
335 |
try: |
|
336 |
connection.simple_bind_s(self.dn, raw_password) |
|
337 |
self._current_password = raw_password |
|
338 |
return True |
|
339 |
except ldap.INVALID_CREDENTIALS: |
|
340 |
return False |
|
341 |
except ldap.LDAPError as e: |
|
342 |
log.warning('LDAPUser.check_password() failed: %r', e) |
|
343 |
if getattr(self, '_current_password', None) is not None: |
|
344 |
return raw_password == self._current_password |
|
345 |
return False |
|
339 | 346 | |
340 | 347 |
def set_password(self, new_password): |
341 | 348 |
# Allow change password to work in all cases, as the form does a check_password() first |
... | ... | |
365 | 372 |
self.ldap_backend.update_default(self.block, validate=False) |
366 | 373 |
return self.ldap_backend.get_connection(self.block, credentials=credentials) |
367 | 374 | |
368 |
def get_attributes(self): |
|
375 |
def get_attributes(self, attribute_source, ctx):
|
|
369 | 376 |
conn = self.get_connection() |
370 |
return self.ldap_backend.get_ldap_attributes(self.block, conn, self.dn) or {} |
|
377 |
key = hashlib.md5((force_text(str(self.pk)) + ';' + force_text(self.dn)).encode('utf-8')).hexdigest() |
|
378 |
# prevents blocking on temporary LDAP failures |
|
379 |
if conn is not None: |
|
380 |
attributes = self.ldap_backend.get_ldap_attributes(self.block, conn, self.dn) or {} |
|
381 |
# keep attributes in cache for 8 hours |
|
382 |
cache.set(key, attributes, 3600 * 8) |
|
383 |
return attributes |
|
384 |
return cache.get(key, {}) |
|
371 | 385 | |
372 | 386 |
def save(self, *args, **kwargs): |
373 | 387 |
if hasattr(self, 'keep_pk'): |
tests/test_ldap.py | ||
---|---|---|
48 | 48 |
CN = 'Étienne Michu' |
49 | 49 |
DN = 'cn=%s,o=ôrga' % escape_dn_chars(CN) |
50 | 50 |
PASS = 'passé' |
51 |
UPASS = u'passé' |
|
51 | 52 |
EMAIL = 'etienne.michu@example.net' |
52 | 53 | |
53 | 54 |
base_dir = os.path.dirname(__file__) |
... | ... | |
836 | 837 |
response = client.post('/login/', {'login-password-submit': '1', |
837 | 838 |
'username': USERNAME, |
838 | 839 |
'password': PASS}, follow=True) |
840 | ||
841 | ||
842 |
def test_get_attributes(slapd, settings, db, rf): |
|
843 |
settings.LDAP_AUTH_SETTINGS = [{ |
|
844 |
'url': [slapd.ldap_url], |
|
845 |
'basedn': u'o=ôrga', |
|
846 |
'use_tls': False, |
|
847 |
}] |
|
848 |
user = authenticate(username=USERNAME, password=UPASS) |
|
849 |
assert user |
|
850 |
assert user.get_attributes(object(), {}) == { |
|
851 |
'dn': u'cn=\xc9tienne Michu,o=\xf4rga', |
|
852 |
'givenname': [u'\xc9tienne'], |
|
853 |
'mail': [u'etienne.michu@example.net'], |
|
854 |
'sn': [u'Michu'], |
|
855 |
'uid': [u'etienne.michu'], |
|
856 |
} |
|
857 |
# simulate LDAP down |
|
858 |
slapd.stop() |
|
859 |
assert user.get_attributes(object(), {}) == { |
|
860 |
'dn': u'cn=\xc9tienne Michu,o=\xf4rga', |
|
861 |
'givenname': [u'\xc9tienne'], |
|
862 |
'mail': [u'etienne.michu@example.net'], |
|
863 |
'sn': [u'Michu'], |
|
864 |
'uid': [u'etienne.michu'], |
|
865 |
} |
|
866 |
assert not user.check_password(UPASS) |
|
867 |
# simulate LDAP come back up |
|
868 |
slapd.start() |
|
869 |
assert user.check_password(UPASS) |
|
870 |
# modify LDAP record and check attributes are updated |
|
871 |
conn = slapd.get_connection_admin() |
|
872 |
ldif = [(ldap.MOD_REPLACE, 'sn', ['Micho'])] |
|
873 |
conn.modify_s(DN, ldif) |
|
874 |
assert user.get_attributes(object(), {}) == { |
|
875 |
'dn': u'cn=\xc9tienne Michu,o=\xf4rga', |
|
876 |
'givenname': [u'\xc9tienne'], |
|
877 |
'mail': [u'etienne.michu@example.net'], |
|
878 |
'sn': [u'Micho'], |
|
879 |
'uid': [u'etienne.michu'], |
|
880 |
} |
|
839 |
- |