0001-LDAPBackend-reactive-user-on-login-synchronization-i.patch
src/authentic2/backends/ldap_backend.py | ||
---|---|---|
37 | 37 |
import os |
38 | 38 |
import random |
39 | 39 |
import time |
40 |
import urllib.parse |
|
41 | 40 | |
42 | 41 |
from django.conf import settings |
43 | 42 |
from django.contrib import messages |
... | ... | |
338 | 337 |
return messages |
339 | 338 | |
340 | 339 | |
340 |
LDAP_DEACTIVATION_REASON_ORPHANED = 'ldap-orphaned' |
|
341 | ||
342 | ||
341 | 343 |
class LDAPUser(User): |
342 | 344 |
SESSION_LDAP_DATA_KEY = 'ldap-data' |
343 | 345 |
_changed = False |
... | ... | |
1477 | 1479 |
if not is_user_authenticable(user): |
1478 | 1480 |
return None |
1479 | 1481 | |
1482 |
if not user.is_active and user.deactivation_reason == LDAP_DEACTIVATION_REASON_ORPHANED: |
|
1483 |
user.mark_as_active() |
|
1484 | ||
1480 | 1485 |
user_login_success(user.get_username()) |
1481 | 1486 |
return user |
1482 | 1487 | |
... | ... | |
1562 | 1567 |
for eid in UserExternalId.objects.filter( |
1563 | 1568 |
external_id__in=eids, user__is_active=True, source=block['realm'] |
1564 | 1569 |
): |
1565 |
eid.user.mark_as_inactive() |
|
1570 |
eid.user.mark_as_inactive(reason=LDAP_DEACTIVATION_REASON_ORPHANED)
|
|
1566 | 1571 |
# Handle users of old sources |
1567 | 1572 |
uei_qs = UserExternalId.objects.exclude(source__in=[block['realm'] for block in cls.get_config()]) |
1568 | 1573 |
for user in User.objects.filter(userexternalid__in=uei_qs): |
1569 |
user.mark_as_inactive() |
|
1574 |
user.mark_as_inactive(reason=LDAP_DEACTIVATION_REASON_ORPHANED)
|
|
1570 | 1575 | |
1571 | 1576 |
@classmethod |
1572 | 1577 |
def ad_encoding(cls, s): |
src/authentic2/custom_user/migrations/0027_user_deactivation_reason.py | ||
---|---|---|
1 |
# Generated by Django 2.2.23 on 2021-05-18 16:14 |
|
2 | ||
3 |
from django.db import migrations, models |
|
4 | ||
5 | ||
6 |
class Migration(migrations.Migration): |
|
7 | ||
8 |
dependencies = [ |
|
9 |
('custom_user', '0026_remove_user_deleted'), |
|
10 |
] |
|
11 | ||
12 |
operations = [ |
|
13 |
migrations.AddField( |
|
14 |
model_name='user', |
|
15 |
name='deactivation_reason', |
|
16 |
field=models.TextField(blank=True, null=True, verbose_name='Deactivation reason'), |
|
17 |
), |
|
18 |
] |
src/authentic2/custom_user/models.py | ||
---|---|---|
177 | 177 |
verbose_name=_('Last account deletion alert'), null=True, blank=True |
178 | 178 |
) |
179 | 179 |
deactivation = models.DateTimeField(verbose_name=_('Deactivation datetime'), null=True, blank=True) |
180 |
deactivation_reason = models.TextField(verbose_name=_('Deactivation reason'), null=True, blank=True) |
|
180 | 181 | |
181 | 182 |
objects = UserManager.from_queryset(UserQuerySet)() |
182 | 183 |
attributes = AttributesDescriptor() |
... | ... | |
360 | 361 |
del self._a2_attributes_cache |
361 | 362 |
return super(User, self).refresh_from_db(*args, **kwargs) |
362 | 363 | |
363 |
def mark_as_inactive(self, timestamp=None): |
|
364 |
def mark_as_active(self): |
|
365 |
self.is_active = True |
|
366 |
self.deactivation = None |
|
367 |
self.deactivation_reason = None |
|
368 |
self.save(update_fields=['is_active', 'deactivation', 'deactivation_reason']) |
|
369 | ||
370 |
def mark_as_inactive(self, timestamp=None, reason=None): |
|
364 | 371 |
self.is_active = False |
365 | 372 |
self.deactivation = timestamp or timezone.now() |
366 |
self.save(update_fields=['is_active', 'deactivation']) |
|
373 |
self.deactivation_reason = reason |
|
374 |
self.save(update_fields=['is_active', 'deactivation', 'deactivation_reason']) |
|
367 | 375 | |
368 | 376 |
def set_random_password(self): |
369 | 377 |
self.set_password(base64.b64encode(os.urandom(32)).decode('ascii')) |
tests/test_ldap.py | ||
---|---|---|
253 | 253 |
conn.delete_s(DN) |
254 | 254 | |
255 | 255 |
ldap_backend.LDAPBackend.deactivate_orphaned_users() |
256 |
list(ldap_backend.LDAPBackend.get_users()) |
|
256 | 257 | |
257 | 258 |
assert ( |
258 |
ldap_backend.UserExternalId.objects.filter(user__is_active=False, source=block['realm']).count() == 1 |
|
259 |
ldap_backend.UserExternalId.objects.filter( |
|
260 |
user__is_active=False, |
|
261 |
source=block['realm'], |
|
262 |
user__deactivation__isnull=False, |
|
263 |
user__deactivation_reason='ldap-orphaned', |
|
264 |
).count() |
|
265 |
== 1 |
|
259 | 266 |
) |
260 | 267 | |
261 | 268 |
# rename source realm |
262 |
settings.LDAP_AUTH_SETTINGS = [ |
|
263 |
{'url': [slapd.ldap_url], 'basedn': 'o=ôrga', 'use_tls': False, 'realm': 'test'} |
|
264 |
] |
|
269 |
settings.LDAP_AUTH_SETTINGS = [] |
|
270 |
ldap_backend.LDAPBackend.deactivate_orphaned_users() |
|
271 |
list(ldap_backend.LDAPBackend.get_users()) |
|
272 | ||
273 |
assert ( |
|
274 |
ldap_backend.UserExternalId.objects.filter( |
|
275 |
user__is_active=False, |
|
276 |
source=block['realm'], |
|
277 |
user__deactivation__isnull=False, |
|
278 |
user__deactivation_reason='ldap-orphaned', |
|
279 |
).count() |
|
280 |
== 6 |
|
281 |
) |
|
265 | 282 | |
283 |
# reactivate users |
|
284 |
settings.LDAP_AUTH_SETTINGS = [block] |
|
285 |
list(ldap_backend.LDAPBackend.get_users()) |
|
266 | 286 |
ldap_backend.LDAPBackend.deactivate_orphaned_users() |
267 | 287 |
assert ( |
268 |
ldap_backend.UserExternalId.objects.filter(user__is_active=False, source=block['realm']).count() == 6 |
|
288 |
ldap_backend.UserExternalId.objects.filter( |
|
289 |
user__is_active=False, |
|
290 |
source=block['realm'], |
|
291 |
user__deactivation__isnull=False, |
|
292 |
user__deactivation_reason='ldap-orphaned', |
|
293 |
).count() |
|
294 |
== 1 |
|
269 | 295 |
) |
296 |
assert ( |
|
297 |
User.objects.filter( |
|
298 |
is_active=True, deactivation_reason__isnull=True, deactivation__isnull=True |
|
299 |
).count() |
|
300 |
== 5 |
|
301 |
) |
|
302 |
assert User.objects.count() == 6 |
|
270 | 303 | |
271 | 304 | |
272 | 305 |
@pytest.mark.django_db |
... | ... | |
1191 | 1224 | |
1192 | 1225 | |
1193 | 1226 |
def test_get_ppolicy_attributes(slapd_ppolicy, settings, db): |
1194 |
settings.LDAP_AUTH_SETTINGS = [{ |
|
1195 |
'url': [slapd_ppolicy.ldap_url], |
|
1196 |
'basedn': u'o=ôrga', |
|
1197 |
'ppolicy_dn': u'cn=default,ou=ppolicies,o=ôrga', |
|
1198 |
'use_tls': False, |
|
1199 |
}] |
|
1227 |
settings.LDAP_AUTH_SETTINGS = [ |
|
1228 |
{ |
|
1229 |
'url': [slapd_ppolicy.ldap_url], |
|
1230 |
'basedn': u'o=ôrga', |
|
1231 |
'ppolicy_dn': u'cn=default,ou=ppolicies,o=ôrga', |
|
1232 |
'use_tls': False, |
|
1233 |
} |
|
1234 |
] |
|
1200 | 1235 | |
1201 | 1236 |
pwdMaxAge = 1 |
1202 |
slapd_ppolicy.add_ldif(''' |
|
1237 |
slapd_ppolicy.add_ldif( |
|
1238 |
''' |
|
1203 | 1239 |
dn: cn=default,ou=ppolicies,o=ôrga |
1204 | 1240 |
cn: default |
1205 | 1241 |
objectclass: top |
... | ... | |
1222 | 1258 |
pwdMustChange: FALSE |
1223 | 1259 |
pwdAllowUserChange: TRUE |
1224 | 1260 |
pwdSafeModify: FALSE |
1225 |
'''.format(pwdMaxAge=pwdMaxAge)) |
|
1261 |
'''.format( |
|
1262 |
pwdMaxAge=pwdMaxAge |
|
1263 |
) |
|
1264 |
) |
|
1226 | 1265 | |
1227 | 1266 |
user = authenticate(username=USERNAME, password=UPASS) |
1228 | 1267 |
assert user.check_password(UPASS) |
1229 |
- |