Projet

Général

Profil

0001-LDAPBackend-reactive-user-on-login-synchronization-i.patch

Benjamin Dauvergne, 18 mai 2021 19:40

Télécharger (7,73 ko)

Voir les différences:

Subject: [PATCH] LDAPBackend: reactive user on login/synchronization if
 inactive (#52670)

 src/authentic2/backends/ldap_backend.py       | 11 +++-
 .../0027_user_deactivation_reason.py          | 18 +++++
 src/authentic2/custom_user/models.py          | 12 +++-
 tests/test_ldap.py                            | 65 +++++++++++++++----
 4 files changed, 88 insertions(+), 18 deletions(-)
 create mode 100644 src/authentic2/custom_user/migrations/0027_user_deactivation_reason.py
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
-