0006-support-ou-selector-in-backends-and-forms-fixes-3025.patch
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 |
- |