Projet

Général

Profil

0006-support-ou-selector-in-backends-and-forms-fixes-3025.patch

Benjamin Dauvergne, 14 février 2019 12:19

Télécharger (9,17 ko)

Voir les différences:

Subject: [PATCH 6/6] support ou selector in backends and forms (fixes #30252)

 src/authentic2/backends/ldap_backend.py   |  8 +++-
 src/authentic2/backends/models_backend.py | 11 +++--
 src/authentic2/forms/__init__.py          | 23 ++++++++-
 tests/test_ldap.py                        | 57 +++++++++++++++++++++++
 tests/test_login.py                       | 14 +++++-
 5 files changed, 106 insertions(+), 7 deletions(-)
src/authentic2/backends/ldap_backend.py
346 346
        log.debug('got config %r', blocks)
347 347
        return blocks
348 348

  
349
    def authenticate(self, username=None, password=None, realm=None):
349
    def authenticate(self, username=None, password=None, realm=None, ou=None, request=None):
350 350
        if username is None or password is None:
351 351
            return None
352 352

  
......
357 357
        if not ldap:
358 358
            raise ImproperlyConfigured('ldap is not available')
359 359

  
360
        default_ou_slug = get_default_ou().slug
361

  
360 362
        # Now we can try to authenticate
361 363
        for block in config:
362 364
            uid = username
365
            # if ou is provided, ignore LDAP server for other OU
366
            if ou:
367
                if ou.slug != (block.get('ou_slug') or default_ou_slug):
368
                    continue
363 369
            if block['limit_to_realm']:
364 370
                if realm is None and '@' in username:
365 371
                    uid, realm = username.rsplit('@', 1)
src/authentic2/backends/models_backend.py
39 39
    Authenticates against settings.AUTH_USER_MODEL.
40 40
    """
41 41

  
42
    def get_query(self, username, realm):
42
    def get_query(self, username, realm=None, ou=None):
43 43
        UserModel = get_user_model()
44 44
        username_field = 'username'
45 45
        queries = []
......
59 59
                            **{username_field: upn(username, realm)}))
60 60
        else:
61 61
            queries.append(models.Q(**{username_field: upn(username, realm)}))
62
        return six.moves.reduce(models.Q.__or__, queries)
62
        queries = six.moves.reduce(models.Q.__or__, queries)
63
        if ou:
64
            queries &= models.Q(ou=ou)
65
        return queries
63 66

  
64 67
    def must_reset_password(self, user):
65 68
        from .. import models
66 69
        return bool(models.PasswordReset.filter(user=user).count())
67 70

  
68
    def authenticate(self, username=None, password=None, realm=None, **kwargs):
71
    def authenticate(self, username=None, password=None, realm=None, ou=None, **kwargs):
69 72
        UserModel = get_user_model()
70 73
        if username is None:
71 74
            username = kwargs.get(UserModel.USERNAME_FIELD)
72 75
        if not username:
73 76
            return
74
        query = self.get_query(username, realm)
77
        query = self.get_query(username=username, realm=realm, ou=ou)
75 78
        users = get_user_queryset().filter(query)
76 79
        # order by username to make username without realm come before usernames with realms
77 80
        # i.e. "toto" should come before "toto@example.com"
src/authentic2/forms/__init__.py
22 22
from django.contrib.auth import REDIRECT_FIELD_NAME, forms as auth_forms
23 23
from django.utils import html
24 24

  
25
from django.contrib.auth import authenticate
26

  
25 27
from django_rbac.utils import get_ou_model
26 28

  
27 29
from authentic2.utils import lazy_label
......
230 232
                raise forms.ValidationError(msg)
231 233

  
232 234
        try:
233
            super(AuthenticationForm, self).clean()
235
            self.clean_authenticate()
234 236
        except Exception:
235 237
            if keys:
236 238
                self.exponential_backoff.failure(*keys)
......
240 242
                self.exponential_backoff.success(*keys)
241 243
        return self.cleaned_data
242 244

  
245
    def clean_authenticate(self):
246
        # copied from django.contrib.auth.forms.AuthenticationForm to add support for ou selector
247
        username = self.cleaned_data.get('username')
248
        password = self.cleaned_data.get('password')
249
        ou = self.cleaned_data.get('ou')
250

  
251
        if username is not None and password:
252
            self.user_cache = authenticate(username=username, password=password, ou=ou, request=self.request)
253
            if self.user_cache is None:
254
                raise forms.ValidationError(
255
                    self.error_messages['invalid_login'],
256
                    code='invalid_login',
257
                    params={'username': self.username_field.verbose_name},
258
                )
259
            else:
260
                self.confirm_login_allowed(self.user_cache)
261

  
262
        return self.cleaned_data
263

  
243 264
    @property
244 265
    def media(self):
245 266
        media = super(AuthenticationForm, self).media
tests/test_ldap.py
700 700
        user = User.objects.get(username=username)
701 701
        assert user.attributes.locality == u'locality%s' % i
702 702
        client.session.flush()
703

  
704

  
705
def test_ou_selector(slapd, settings, app, ou1):
706
    settings.LDAP_AUTH_SETTINGS = [{
707
        'url': [slapd.ldap_url],
708
        'binddn': force_text(DN),
709
        'bindpw': PASS,
710
        'basedn': u'o=ôrga',
711
        'ou_slug': ou1.slug,
712
        'use_tls': False,
713
    }]
714
    settings.A2_LOGIN_FORM_OU_SELECTOR = True
715

  
716
    # Check login to the wrong ou does not work
717
    response = app.get('/login/')
718
    response.form.set('username', USERNAME)
719
    response.form.set('password', PASS)
720
    response.form.set('ou', str(get_default_ou().pk))
721
    response = response.form.submit(name='login-password-submit')
722
    assert response.pyquery('.errorlist.nonfield')
723
    assert '_auth_user_id' not in app.session
724

  
725
    # Check login to the proper ou works
726
    response = app.get('/login/')
727
    response.form.set('username', USERNAME)
728
    response.form.set('password', PASS)
729
    response.form.set('ou', str(ou1.pk))
730
    response = response.form.submit(name='login-password-submit').follow()
731
    assert '_auth_user_id' in app.session
732

  
733

  
734
def test_ou_selector_default_ou(slapd, settings, app, ou1):
735
    settings.LDAP_AUTH_SETTINGS = [{
736
        'url': [slapd.ldap_url],
737
        'binddn': force_text(DN),
738
        'bindpw': PASS,
739
        'basedn': u'o=ôrga',
740
        'use_tls': False,
741
    }]
742
    settings.A2_LOGIN_FORM_OU_SELECTOR = True
743

  
744
    # Check login to the wrong ou does not work
745
    response = app.get('/login/')
746
    response.form.set('username', USERNAME)
747
    response.form.set('password', PASS)
748
    response.form.set('ou', str(ou1.pk))
749
    response = response.form.submit(name='login-password-submit')
750
    assert response.pyquery('.errorlist.nonfield')
751
    assert '_auth_user_id' not in app.session
752

  
753
    # Check login to the proper ou works
754
    response = app.get('/login/')
755
    response.form.set('username', USERNAME)
756
    response.form.set('password', PASS)
757
    response.form.set('ou', str(get_default_ou().pk))
758
    response = response.form.submit(name='login-password-submit').follow()
759
    assert '_auth_user_id' in app.session
tests/test_login.py
135 135
    assert simple_user.first_name not in response
136 136

  
137 137

  
138
def test_ou_selector(app, settings, simple_user):
138
def test_ou_selector(app, settings, simple_user, ou1):
139 139
    settings.A2_LOGIN_FORM_OU_SELECTOR = True
140 140
    response = app.get('/login/')
141 141
    # Check selector is here and there are no errors
......
148 148
    response.form.set('password', simple_user.username)
149 149
    response = response.form.submit(name='login-password-submit')
150 150
    assert response.pyquery('.errorlist')
151
    # Check login to the wrong ou do not work
152
    response.form.set('password', simple_user.username)
153
    response.form.set('ou', str(ou1.pk))
154
    response = response.form.submit(name='login-password-submit')
155
    assert not response.pyquery('.errorlist:not(.nonfield)')
156
    assert response.pyquery('.errorlist.nonfield')
157
    assert '_auth_user_id' not in app.session
158
    # Check login to the proper ou works
159
    response.form.set('password', simple_user.username)
160
    response.form.set('ou', str(simple_user.ou.pk))
161
    response = response.form.submit(name='login-password-submit').follow()
162
    assert '_auth_user_id' in app.session
151
-