0002-misc-add-support-for-request-based-enable-conditions.patch
src/authentic2/app_settings.py | ||
---|---|---|
157 | 157 |
definition='path of a class to validate passwords'), |
158 | 158 |
A2_PASSWORD_POLICY_SHOW_LAST_CHAR=Setting(default=False, definition='Show last character in password fields'), |
159 | 159 |
A2_AUTH_PASSWORD_ENABLE=Setting(default=True, definition='Activate login/password authentication', names=('AUTH_PASSWORD',)), |
160 |
A2_AUTH_PASSWORD_ENABLE_CONDITION=Setting(default=None, definition='Filters', names=('FRONTEND ENABLE CONDITION',)), |
|
160 | 161 |
A2_LOGIN_FAILURE_COUNT_BEFORE_WARNING=Setting(default=0, |
161 | 162 |
definition='Failure count before logging a warning to ' |
162 | 163 |
'authentic2.user_login_failure. No warning will be send if value is ' |
src/authentic2/auth2_auth/auth2_ssl/app_settings.py | ||
---|---|---|
16 | 16 |
CREATE_USERNAME_CALLBACK=None, |
17 | 17 |
USE_COOKIE=False, |
18 | 18 |
CREATE_USER=False, |
19 |
ENABLE_CONDITION=None |
|
19 | 20 |
) |
20 | 21 | |
21 | 22 |
def __init__(self, prefix): |
src/authentic2/auth2_auth/auth2_ssl/frontends.py | ||
---|---|---|
3 | 3 | |
4 | 4 |
from . import views, app_settings |
5 | 5 |
from authentic2.utils import redirect_to_login |
6 |
from authentic2.auth_frontends import BaseFrontend |
|
6 | 7 | |
7 | 8 | |
8 |
class SSLFrontend(object): |
|
9 |
def enabled(self): |
|
10 |
return app_settings.ENABLE |
|
9 |
class SSLFrontend(BaseFrontend): |
|
10 |
def enabled(self, request=None): |
|
11 |
if app_settings.ENABLE: |
|
12 |
return self.eval_enable_condition(app_settings.ENABLE_CONDITION, request) |
|
13 |
return False |
|
11 | 14 | |
12 | 15 |
def id(self): |
13 | 16 |
return 'ssl' |
src/authentic2/auth_frontends.py | ||
---|---|---|
1 |
import logging |
|
2 | ||
1 | 3 |
from django.shortcuts import render |
2 | 4 |
from django.utils.translation import ugettext as _, ugettext_lazy |
3 | 5 | |
4 | 6 |
from . import views, app_settings, utils, constants, forms |
5 | 7 | |
6 | 8 | |
7 |
class LoginPasswordBackend(object): |
|
9 |
class BaseFrontend(object): |
|
10 |
def eval_enable_condition(self, condition, request, **kwargs): |
|
11 |
if condition: |
|
12 |
try: |
|
13 |
context = {'request': request} |
|
14 |
if kwargs: |
|
15 |
context.update(kwargs) |
|
16 |
return eval(condition, context) |
|
17 |
except Exception, e: |
|
18 |
logging.getLogger(__name__).warning('filter expression error: %r' % e) |
|
19 |
return False |
|
20 |
return True |
|
21 | ||
22 | ||
23 |
class LoginPasswordBackend(BaseFrontend): |
|
24 | ||
8 | 25 |
submit_name = 'login-password-submit' |
9 | 26 | |
10 |
def enabled(self): |
|
11 |
return app_settings.A2_AUTH_PASSWORD_ENABLE |
|
27 |
def enabled(self, request=None): |
|
28 |
if app_settings.A2_AUTH_PASSWORD_ENABLE: |
|
29 |
return self.eval_enable_condition(app_settings.A2_AUTH_PASSWORD_ENABLE_CONDITION, request) |
|
30 |
return False |
|
12 | 31 | |
13 | 32 |
def name(self): |
14 | 33 |
return ugettext_lazy('Password') |
src/authentic2/registration_backend/views.py | ||
---|---|---|
95 | 95 |
parameters = {'request': self.request, |
96 | 96 |
'context': context} |
97 | 97 |
blocks = [utils.get_backend_method(backend, 'registration', parameters) |
98 |
for backend in utils.get_backends('AUTH_FRONTENDS')] |
|
98 |
for backend in utils.get_backends('AUTH_FRONTENDS', self.request)]
|
|
99 | 99 |
context['frontends'] = collections.OrderedDict((block['id'], block) |
100 | 100 |
for block in blocks if block) |
101 | 101 |
return context |
src/authentic2/utils.py | ||
---|---|---|
152 | 152 |
return cls() |
153 | 153 | |
154 | 154 | |
155 |
def get_backends(setting_name='IDP_BACKENDS'): |
|
155 |
def get_backends(setting_name='IDP_BACKENDS', request=None):
|
|
156 | 156 |
'''Return the list of enabled cleaned backends.''' |
157 | 157 |
backends = [] |
158 | 158 |
for backend_path in getattr(app_settings, setting_name): |
... | ... | |
161 | 161 |
backend_path, kwargs = backend_path |
162 | 162 |
backend = load_backend(backend_path) |
163 | 163 |
# If no enabled method is defined on the backend, backend enabled by default. |
164 |
if hasattr(backend, 'enabled') and not backend.enabled(): |
|
164 |
if hasattr(backend, 'enabled') and not backend.enabled(request):
|
|
165 | 165 |
continue |
166 | 166 |
kwargs_settings = getattr(app_settings, setting_name + '_KWARGS', {}) |
167 | 167 |
if backend_path in kwargs_settings: |
src/authentic2/views.py | ||
---|---|---|
281 | 281 |
redirect_to = settings.LOGIN_REDIRECT_URL |
282 | 282 |
nonce = request.GET.get(constants.NONCE_FIELD_NAME) |
283 | 283 | |
284 |
frontends = utils.get_backends('AUTH_FRONTENDS') |
|
284 |
frontends = utils.get_backends('AUTH_FRONTENDS', request)
|
|
285 | 285 | |
286 | 286 |
blocks = [] |
287 | 287 | |
... | ... | |
403 | 403 |
return super(ProfileView, self).dispatch(request, *args, **kwargs) |
404 | 404 | |
405 | 405 |
def get_context_data(self, **kwargs): |
406 |
context = super(ProfileView, self).get_context_data(**kwargs) |
|
407 |
frontends = utils.get_backends('AUTH_FRONTENDS') |
|
408 | ||
409 | 406 |
request = self.request |
410 | 407 | |
408 |
context = super(ProfileView, self).get_context_data(**kwargs) |
|
409 |
frontends = utils.get_backends('AUTH_FRONTENDS', request) |
|
410 | ||
411 | 411 |
if request.method == "POST": |
412 | 412 |
for frontend in frontends: |
413 | 413 |
if 'submit-%s' % frontend.id in request.POST: |
src/authentic2_auth_oidc/app_settings.py | ||
---|---|---|
18 | 18 |
def ENABLE(self): |
19 | 19 |
return self._setting('ENABLE', True) |
20 | 20 | |
21 |
@property |
|
22 |
def IDP_ENABLE_CONDITION(self): |
|
23 |
return self._setting('IDP_ENABLE_CONDITION', None) |
|
24 | ||
21 | 25 | |
22 | 26 |
import sys |
23 | 27 |
src/authentic2_auth_oidc/auth_frontends.py | ||
---|---|---|
2 | 2 |
from django.shortcuts import render |
3 | 3 | |
4 | 4 |
from . import app_settings, utils |
5 |
from authentic2.auth_frontends import BaseFrontend |
|
5 | 6 | |
6 | 7 | |
7 |
class OIDCFrontend(object):
|
|
8 |
def enabled(self): |
|
8 |
class OIDCFrontend(BaseFrontend):
|
|
9 |
def enabled(self, request=None):
|
|
9 | 10 |
return app_settings.ENABLE and utils.has_providers() |
10 | 11 | |
11 | 12 |
def name(self): |
... | ... | |
16 | 17 | |
17 | 18 |
def login(self, request, *args, **kwargs): |
18 | 19 |
context = kwargs.get('context', {}) |
19 |
context['providers'] = utils.get_providers(shown=True) |
|
20 |
providers = utils.get_providers(shown=True) |
|
21 |
if app_settings.IDP_ENABLE_CONDITION is not None: |
|
22 |
providers = self.eval_enable_condition(app_settings.IDP_ENABLE_CONDITION, request, |
|
23 |
providers=providers) |
|
24 |
context['providers'] = providers |
|
20 | 25 |
return render(request, 'authentic2_auth_oidc/login.html', context) |
src/authentic2_auth_saml/app_settings.py | ||
---|---|---|
19 | 19 |
def enable(self): |
20 | 20 |
return self._setting('ENABLE', False) |
21 | 21 | |
22 | ||
23 | 22 |
import sys |
24 | 23 | |
25 | 24 |
app_settings = AppSettings('A2_AUTH_SAML_') |
src/authentic2_auth_saml/auth_frontends.py | ||
---|---|---|
4 | 4 |
from mellon.utils import get_idp, get_idps |
5 | 5 | |
6 | 6 |
from authentic2.utils import redirect_to_login |
7 |
from authentic2.auth_frontends import BaseFrontend |
|
7 | 8 | |
8 | 9 |
from . import app_settings |
9 | 10 | |
10 | 11 | |
11 |
class SAMLFrontend(object):
|
|
12 |
class SAMLFrontend(BaseFrontend):
|
|
12 | 13 |
id = 'saml' |
13 | 14 | |
14 |
def enabled(self): |
|
15 |
return app_settings.enable and list(get_idps()) |
|
15 |
def enabled(self, request=None): |
|
16 |
if app_settings.enable: |
|
17 |
return self.eval_enable_condition(app_settings.enable_condition, request) |
|
18 |
return False |
|
16 | 19 | |
17 | 20 |
def name(self): |
18 | 21 |
return gettext_noop('SAML') |
tests/test_login.py | ||
---|---|---|
3 | 3 | |
4 | 4 |
from django.contrib.auth import get_user_model |
5 | 5 | |
6 |
from utils import login |
|
6 |
from utils import login, check_log
|
|
7 | 7 | |
8 | 8 | |
9 | 9 |
def test_login_inactive_user(db, app): |
... | ... | |
31 | 31 |
login(app, user1) |
32 | 32 |
assert '_auth_user_id' not in app.session |
33 | 33 | |
34 |
def test_login_with_conditionnal_enabled_frontend(db, app, settings, caplog): |
|
35 |
settings.A2_AUTH_PASSWORD_ENABLE_CONDITION = "request.META['REMOTE_ADDR'] in ('127.0.0.1',)" |
|
36 |
response = app.get('/login/') |
|
37 |
assert 'name="login-password-submit"' in response |
|
38 | ||
39 |
# set invalid display condition |
|
40 |
settings.A2_AUTH_PASSWORD_ENABLE_CONDITION = "request.META['REMOTE_ADDR'] in ('0.0.0.0',)" |
|
41 |
response = app.get('/login/') |
|
42 |
# login form must not be displayed |
|
43 |
assert 'name="login-password-submit"' not in response |
|
44 |
assert len(caplog.records) == 0 |
|
45 | ||
46 |
# set a condition with error |
|
47 |
with check_log(caplog, 'filter expression error: SyntaxError(\'unexpected EOF while parsing\''): |
|
48 |
settings.A2_AUTH_PASSWORD_ENABLE_CONDITION = "request.META['REMOTE_ADDR'] in " |
|
49 |
response = app.get('/login/') |
|
50 |
assert 'name="login-password-submit"' not in response |
|
51 | ||
34 | 52 | |
35 | 53 |
def test_registration_url_on_login_page(db, app): |
36 | 54 |
response = app.get('/login/?next=/whatever') |
37 |
- |